Skip to content

Commit

Permalink
[Courgette] Refactor: Add CourgetteFlow; improve courgette_tool.cc he…
Browse files Browse the repository at this point in the history
…lp text.

High level flows in Courgette has messy error-handling code that's
hard to read. This CL adds CourgetteFlow to absorb commonly used data
and error-handling code, and improve readability of high-level logic.
Additional changes:
- Fix Courgette-disadj (debugging flow), which likely was broken by
  http://crrev.com/2793153003; Adjust() is called but AssemblyProgram
  did not annotate Labels.
- Add BasicBuffer interface, implemented by RegionBuffer and (existing)
  BufferedFileReader.
- PatchGeneratorX86_32::Transform(): Implemented TODO to instantiate
  "new" AssemblyProgram to reduce Courgette-gen peak memory.
- courgette_tools.cc: Make help text more uniform.

BUG=660980

Review-Url: https://codereview.chromium.org/2827103002
Cr-Commit-Position: refs/heads/master@{#467157}
  • Loading branch information
samuelhuang authored and Commit bot committed Apr 25, 2017
1 parent 7fa4585 commit 8b91a4c
Show file tree
Hide file tree
Showing 9 changed files with 604 additions and 346 deletions.
2 changes: 2 additions & 0 deletions courgette/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ static_library("courgette_lib") {
"assembly_program.h",
"consecutive_range_visitor.h",
"courgette.h",
"courgette_flow.cc",
"courgette_flow.h",
"crc.cc",
"crc.h",
"difference_estimator.cc",
Expand Down
189 changes: 189 additions & 0 deletions courgette/courgette_flow.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// 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 "courgette/courgette_flow.h"

#include <stdarg.h>

#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "courgette/assembly_program.h"
#include "courgette/encoded_program.h"
#include "courgette/program_detector.h"

namespace courgette {

/******** CourgetteFlow::Data ********/

CourgetteFlow::Data::Data() = default;

CourgetteFlow::Data::~Data() = default;

/******** CourgetteFlow ********/

CourgetteFlow::CourgetteFlow() = default;

CourgetteFlow::~CourgetteFlow() = default;

// static
const char* CourgetteFlow::name(Group group) {
switch (group) {
case ONLY:
return "input";
case OLD:
return "'old' input";
case NEW:
return "'new' input";
default:
NOTREACHED();
break;
}
return nullptr;
}

CourgetteFlow::Data* CourgetteFlow::data(Group group) {
switch (group) {
case ONLY:
return &data_only_;
case OLD:
return &data_old_;
case NEW:
return &data_new_;
default:
NOTREACHED();
break;
}
return nullptr;
}

bool CourgetteFlow::ok() {
return status_ == C_OK;
}

bool CourgetteFlow::failed() {
return status_ != C_OK;
}

Status CourgetteFlow::status() {
return status_;
}

const std::string& CourgetteFlow::message() {
return message_;
}

void CourgetteFlow::ReadSourceStreamSetFromBuffer(Group group,
const BasicBuffer& buffer) {
if (failed())
return;
Data* d = data(group);
if (!check(d->sources.Init(buffer.data(), buffer.length()),
C_GENERAL_ERROR)) {
setMessage("Cannot read %s as SourceStreamSet.", name(group));
}
}

void CourgetteFlow::ReadAssemblyProgramFromBuffer(Group group,
const BasicBuffer& buffer,
bool annotate) {
if (failed())
return;
Data* d = data(group);
auto parser = annotate ? ParseDetectedExecutableWithAnnotation
: ParseDetectedExecutable;
if (!check(parser(buffer.data(), buffer.length(), &d->program)))
setMessage("Cannot parse %s (code = %d).", name(group), status_);
}

void CourgetteFlow::ReadEncodedProgramFromSourceStreamSet(
Group group,
SourceStreamSet* opt_sources /* nullptr */) {
if (failed())
return;
Data* d = data(group);
SourceStreamSet* sources = opt_sources ? opt_sources : &d->sources;
if (!check(ReadEncodedProgram(sources, &d->encoded)))
setMessage("Cannot read %s as encoded program.", name(group));
}

void CourgetteFlow::CreateEncodedProgramFromAssemblyProgram(Group group) {
if (failed())
return;
Data* d = data(group);
if (!check(Encode(*d->program, &d->encoded)))
setMessage("Cannot encode %s (code = %d).", name(group), status_);
}

void CourgetteFlow::WriteSinkStreamFromSinkStreamSet(Group group,
SinkStream* sink) {
DCHECK(sink);
if (failed())
return;
if (!check(data(group)->sinks.CopyTo(sink), C_GENERAL_ERROR))
setMessage("Cannnot combine serialized streams for %s.", name(group));
}

void CourgetteFlow::WriteSinkStreamSetFromEncodedProgram(
Group group,
SinkStreamSet* opt_sinks /* nullptr */) {
if (failed())
return;
Data* d = data(group);
SinkStreamSet* sinks = opt_sinks ? opt_sinks : &d->sinks;
if (!check(WriteEncodedProgram(d->encoded.get(), sinks)))
setMessage("Cannot serialize encoded %s.", name(group));
}

void CourgetteFlow::WriteExecutableFromEncodedProgram(Group group,
SinkStream* sink) {
DCHECK(sink);
if (failed())
return;
if (!check(Assemble(data(group)->encoded.get(), sink)))
setMessage("Cannot assemble %s.", name(group));
}

void CourgetteFlow::AdjustNewAssemblyProgramToMatchOld() {
if (failed())
return;
if (!check(Adjust(*data_old_.program, data_new_.program.get())))
setMessage("Cannot adjust %s to match %s.", name(OLD), name(NEW));
}

void CourgetteFlow::DestroyAssemblyProgram(Group group) {
if (failed())
return;
data(group)->program.reset();
}

void CourgetteFlow::DestroyEncodedProgram(Group group) {
if (failed())
return;
data(group)->encoded.reset();
}

bool CourgetteFlow::check(Status new_status) {
if (new_status == C_OK)
return true;
status_ = new_status;
return false;
}

bool CourgetteFlow::check(bool success, Status failure_mode) {
if (success)
return true;
status_ = failure_mode;
return false;
}

void CourgetteFlow::setMessage(const char* format, ...) {
va_list args;
va_start(args, format);
message_ = base::StringPrintV(format, args);
va_end(args);
}

} // namespace courgette
144 changes: 144 additions & 0 deletions courgette/courgette_flow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// 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 COURGETTE_COURGETTE_FLOW_H_
#define COURGETTE_COURGETTE_FLOW_H_

#include <memory>
#include <string>

#include "base/macros.h"
#include "courgette/courgette.h"
#include "courgette/region.h"
#include "courgette/streams.h"

namespace courgette {

class AssemblyProgram;
class EncodedProgram;

// An adaptor for Region as BasicBuffer.
class RegionBuffer : public BasicBuffer {
public:
explicit RegionBuffer(const Region& region) : region_(region) {}
~RegionBuffer() override {}

// BasicBuffer:
const uint8_t* data() const override { return region_.start(); }
size_t length() const override { return region_.length(); }

private:
Region region_;

DISALLOW_COPY_AND_ASSIGN(RegionBuffer);
};

// CourgetteFlow stores Courgette data arranged into groups, and exposes
// "commands" that operate on them. On the first occurrence of an error, the
// Courgette error code is recorded, error messages are generated and stored,
// and all subsequent commands become no-op. This allows callers to concisely
// specify high-level logic with minimal code for error handling.
class CourgetteFlow {
public:
// A group of Courgette data, for a single executable. Takes negligible space
// when unused.
struct Data {
Data();
~Data();

std::unique_ptr<AssemblyProgram> program;
std::unique_ptr<EncodedProgram> encoded;
SinkStreamSet sinks;
SourceStreamSet sources;
};

// Group enumeration into |data_*_| fields.
enum Group {
ONLY, // The only file processed.
OLD, // The "old" file during patching.
NEW, // The "new" file during patching.
};

CourgetteFlow();
~CourgetteFlow();

static const char* name(Group group);
Data* data(Group group); // Allows caller to modify.
bool ok();
bool failed();
Status status();
const std::string& message();

// Commands that perform no-op on error. This allows caller to concisely
// specify high-level logic, and perform a single error check at the end. Care
// must be taken w.r.t. error handling if |data()| is harvested between
// commands.

// Reads |buffer| to initialize |data(group)->sources|.
void ReadSourceStreamSetFromBuffer(Group group, const BasicBuffer& buffer);

// Reads |buffer| to initialize |data(group)->program|, passing |annotate| as
// initialization parameter (true if AdjustNewAssemblyProgramToMatchOld() gets
// called later).
void ReadAssemblyProgramFromBuffer(Group group,
const BasicBuffer& buffer,
bool annotate);

// Reads |opt_sources| if given, or else |data(group)->sources| to initialize
// |data(group).encoded|.
void ReadEncodedProgramFromSourceStreamSet(
Group group,
SourceStreamSet* opt_sources = nullptr);

// Uses |data(group)->program| to initialize |data(group)->encoded|.
void CreateEncodedProgramFromAssemblyProgram(Group group);

// Serializese |data(group)->sinks| to |sink|.
void WriteSinkStreamFromSinkStreamSet(Group group, SinkStream* sink);

// Serializes |data(group)->encoded| to |opt_sinks| if given, or else to
// |data(group)->sinks|.
void WriteSinkStreamSetFromEncodedProgram(Group group,
SinkStreamSet* opt_sinks = nullptr);

// Converts |data(group)->encoded| to an exectuable and writes the result to
// |sink|.
void WriteExecutableFromEncodedProgram(Group group, SinkStream* sink);

// Adjusts |data(NEW)->program| Labels to match |data(OLD)->program| Labels.
void AdjustNewAssemblyProgramToMatchOld();

// Destructor commands to reduce memory usage.

void DestroyAssemblyProgram(Group group);

void DestroyEncodedProgram(Group group);

private:
// Utilities to process return values from Courgette functions, and assign
// |status_| and |message_|. Usage:
// if (!check(some_courgette_function(param1, ...)))
// setMessage("format string %s...", value1, ...);

// Reassigns |status_|, and returns true if |C_OK|.
bool check(Status new_status);

// check() alternative for functions that return true on succes. On failure
// assigns |status_| to |failure_mode|.
bool check(bool success, Status failure_mode);

void setMessage(const char* format, ...);

Status status_ = C_OK;
std::string message_;
Data data_only_;
Data data_old_;
Data data_new_;

DISALLOW_COPY_AND_ASSIGN(CourgetteFlow);
};

} // namespace courgette

#endif // COURGETTE_COURGETTE_FLOW_H_
Loading

0 comments on commit 8b91a4c

Please sign in to comment.