Skip to content

Commit

Permalink
[Zucchini] Add command line processing framework with stubs.
Browse files Browse the repository at this point in the history
This CL adds main_utils.*, which has the framework to dispatch Zucchini
commands and print help messages. This is used by the Zucchini main
program, and is not part of Zucchini library.
- Add ::Command: A struct containing command name (e.g., "-gen",
  "-apply"), help message, and a base::Callback containing code for
  the main command. This allows Zucchini command dispatch without bulky
  bulky class hierarchy and superfluous instantialization.
- Add ::CommandRegistry: A class to manage ::Command instances and
  dispatch commands, or print help messages.
- Add ::ResourceUsageTracker: A class instantiated in main() to track
  resource usage for valid command invocations, and and print them at
  end. Specifically:
  - Zucchini.PeakPagefileUsage (KiB)
  - Zucchini.PeakWorkingSetSize (KiB)
  - Zucchini.TotalTime (s)
  Also add "-quiet" switch to disable this.
- Add stubs for Zucchini-gen and Zucchini-apply, to be populated later.
- Starting to use LOG(INFO) to display output. This requires updating
  PRESUBMIT.py to add Zucchini as an exception.

Bug: 729154
Change-Id: Id5263435100dc73b2d8917ca2bcd04245ed41934
Reviewed-on: https://chromium-review.googlesource.com/567492
Commit-Queue: Samuel Huang <huangs@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#486762}
  • Loading branch information
samuelhuang authored and Commit Bot committed Jul 14, 2017
1 parent a308bcc commit a13b5a0
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 0 deletions.
1 change: 1 addition & 0 deletions PRESUBMIT.py
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,7 @@ def _CheckSpamLogging(input_api, output_api):
r"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]"
r"startup_browser_creator\.cc$",
r"^chrome[\\\/]installer[\\\/]setup[\\\/].*",
r"^chrome[\\\/]installer[\\\/]zucchini[\\\/].*",
r"chrome[\\\/]browser[\\\/]diagnostics[\\\/]" +
r"diagnostics_writer\.cc$",
r"^chrome_elf[\\\/]dll_hash[\\\/]dll_hash_main\.cc$",
Expand Down
4 changes: 4 additions & 0 deletions chrome/installer/zucchini/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ static_library("zucchini_lib") {
"image_utils.h",
"io_utils.cc",
"io_utils.h",
"main_utils.cc",
"main_utils.h",
"suffix_array.h",
"typed_value.h",
]
Expand All @@ -30,6 +32,8 @@ executable("zucchini") {
]

deps = [
":zucchini_lib",
"//base",
"//build/config:exe_and_shlib_deps",
]

Expand Down
131 changes: 131 additions & 0 deletions chrome/installer/zucchini/main_utils.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/installer/zucchini/main_utils.h"

#include <iostream>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/process/process_metrics.h"
#include "build/build_config.h"
#include "chrome/installer/zucchini/io_utils.h"

namespace {

// Translates |command_line| arguments to a vector of base::FilePath and returns
// the result via |fnames|. Expects exactly |expected_count|.
bool CheckAndGetFilePathParams(const base::CommandLine& command_line,
size_t expected_count,
std::vector<base::FilePath>* fnames) {
const base::CommandLine::StringVector& args = command_line.GetArgs();
if (args.size() != expected_count)
return false;

fnames->clear();
for (size_t i = 0; i < args.size(); ++i)
fnames->push_back(base::FilePath(args[i]));
return true;
}

} // namespace

/******** ResourceUsageTracker ********/

ResourceUsageTracker::ResourceUsageTracker() : start_time_(base::Time::Now()) {}

ResourceUsageTracker::~ResourceUsageTracker() {
base::Time end_time = base::Time::Now();

#if !defined(OS_MACOSX)
std::unique_ptr<base::ProcessMetrics> process_metrics(
base::ProcessMetrics::CreateProcessMetrics(
base::GetCurrentProcessHandle()));

LOG(INFO) << "Zucchini.PeakPagefileUsage "
<< process_metrics->GetPeakPagefileUsage() / 1024 << " KiB";
LOG(INFO) << "Zucchini.PeakWorkingSetSize "
<< process_metrics->GetPeakWorkingSetSize() / 1024 << " KiB";
#endif // !defined(OS_MACOSX)

LOG(INFO) << "Zucchini.TotalTime " << (end_time - start_time_).InSecondsF()
<< " s";
}

/******** Command ********/

Command::Command(const char* name_in,
const char* usage_in,
int num_args_in,
Command::Fun fun_in)
: name(name_in), usage(usage_in), num_args(num_args_in), fun(fun_in) {}

Command::Command(const Command&) = default;

Command::~Command() = default;

/******** CommandRegistry ********/

CommandRegistry::CommandRegistry() = default;

CommandRegistry::~CommandRegistry() = default;

void CommandRegistry::Register(const Command* command) {
commands_.push_back(command);
}

void CommandRegistry::RunOrExit(const base::CommandLine& command_line) {
const Command* command_use = nullptr;
for (const Command* command : commands_) {
if (command_line.HasSwitch(command->name)) {
if (command_use) { // Too many commands found.
command_use = nullptr; // Set to null to flag error.
break;
}
command_use = command;
}
}

// If we don't have exactly one matching command, print error and exit.
if (!command_use) {
std::cerr << "Must have exactly one of:\n [";
zucchini::PrefixSep sep(", ");
for (const Command* command : commands_)
std::cerr << sep << "-" << command->name;
std::cerr << "]" << std::endl;
PrintUsageAndExit();
}

std::vector<base::FilePath> fnames;
if (CheckAndGetFilePathParams(command_line, command_use->num_args, &fnames)) {
command_use->fun.Run(command_line, fnames);
} else {
std::cerr << command_use->usage << std::endl;
PrintUsageAndExit();
}
}

void CommandRegistry::PrintUsageAndExit() {
std::cerr << "Usage:" << std::endl;
for (const Command* command : commands_)
std::cerr << " zucchini " << command->usage << std::endl;
exit(1);
}

/******** Command Definitions ********/

Command kCommandGen = {
"gen", "-gen <old_file> <new_file> <patch_file>", 3,
base::Bind([](const base::CommandLine& command_line,
const std::vector<base::FilePath>& fnames) -> void {
// TODO(etiennep): Implement.
})};

Command kCommandApply = {
"apply", "-apply <old_file> <patch_file> <new_file>", 3,
base::Bind([](const base::CommandLine& command_line,
const std::vector<base::FilePath>& fnames) -> void {
// TODO(etiennep): Implement.
})};
86 changes: 86 additions & 0 deletions chrome/installer/zucchini/main_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_INSTALLER_ZUCCHINI_MAIN_UTILS_H_
#define CHROME_INSTALLER_ZUCCHINI_MAIN_UTILS_H_

#include <memory>
#include <vector>

#include "base/callback.h"
#include "base/macros.h"
#include "base/time/time.h"

namespace base {

class CommandLine;
class FilePath;

} // namespace base

// Class to track and print system resource usage. Should be instantiated early
// in program flow to better track start time.
class ResourceUsageTracker {
public:
ResourceUsageTracker();
~ResourceUsageTracker();

private:
base::Time start_time_;

DISALLOW_COPY_AND_ASSIGN(ResourceUsageTracker);
};

// Specs for a Zucchini command.
struct Command {
using Fun = base::Callback<void(const base::CommandLine&,
const std::vector<base::FilePath>&)>;

Command(const char* name_in,
const char* usage_in,
int num_args_in,
Fun fun_in);
explicit Command(const Command&);
~Command();

// Unique name of command. |-name| is used to select from command line.
const char* name;

// Usage help text of command.
const char* usage;

// Number of arguments (assumed to be filenames) used by the command.
const int num_args;

// Main code for the command.
Fun fun;
};

// Registry of Commands to select the command to run and to handle errors.
class CommandRegistry {
public:
CommandRegistry();
~CommandRegistry();

void Register(const Command* command);

// Uses |command_line| to find a Command instance. If a unique command is
// found, then runs it. Otherwise prints error and exits.
void RunOrExit(const base::CommandLine& command_line);

private:
void PrintUsageAndExit();

std::vector<const Command*> commands_;

DISALLOW_COPY_AND_ASSIGN(CommandRegistry);
};

// Command: Patch generation.
extern Command kCommandGen;

// Command: Patch application.
extern Command kCommandApply;

#endif // CHROME_INSTALLER_ZUCCHINI_MAIN_UTILS_H_
31 changes: 31 additions & 0 deletions chrome/installer/zucchini/zucchini_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/logging.h"
#include "chrome/installer/zucchini/main_utils.h"

namespace {

void InitLogging() {
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
settings.log_file = nullptr;
settings.lock_log = logging::DONT_LOCK_LOG_FILE;
settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
bool logging_res = logging::InitLogging(settings);
CHECK(logging_res);
}

} // namespace

int main(int argc, const char* argv[]) {
ResourceUsageTracker tracker;
base::CommandLine::Init(argc, argv);
InitLogging();

const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();

// Instantiate Command registry and register Zucchini features.
CommandRegistry registry;
registry.Register(&kCommandGen);
registry.Register(&kCommandApply);

registry.RunOrExit(command_line);
return 0;
}

0 comments on commit a13b5a0

Please sign in to comment.