forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Courgette] Refactor: Add CourgetteFlow; improve courgette_tool.cc he…
…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
1 parent
7fa4585
commit 8b91a4c
Showing
9 changed files
with
604 additions
and
346 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
Oops, something went wrong.