blob: 7c8866726302eb9de566daf9ad9409c4340f3b29 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This defines the pure virtual Reorderer base class. This class abstracts
// away the ETW log parsing, decomposition, Block lookup, etc, that is a routine
// part of producing a new ordering. Derived classes are to implement actual
// order generation.
#ifndef SYZYGY_REORDER_REORDERER_H_
#define SYZYGY_REORDER_REORDERER_H_
#include <windows.h> // NOLINT
#include <dbghelp.h>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "base/win/event_trace_consumer.h"
#include "syzygy/pe/decomposer.h"
#include "syzygy/pe/image_layout.h"
#include "syzygy/playback/playback.h"
#include "syzygy/trace/parse/parser.h"
// Forward declaration.
namespace core {
class JSONFileWriter;
} // namespace core
namespace reorder {
typedef uint64_t AbsoluteAddress64;
typedef uint64_t Size64;
// This class can consume a set of call-trace logs captured for a PE image
// while driving an OrderGenerator instance to produce an ordering file.
class Reorderer : public trace::parser::ParseEventHandlerImpl {
public:
typedef trace::parser::Parser Parser;
typedef pe::ImageLayout ImageLayout;
typedef pe::PEFile PEFile;
typedef std::vector<base::FilePath> TraceFileList;
struct Order;
class OrderGenerator;
class UniqueTime;
// A bit flag of directives that the derived reorderer should attempt
// to satisfy.
// TODO(chrisha): Basic block reordering.
enum FlagsEnum {
kFlagReorderCode = 1 << 0,
kFlagReorderData = 1 << 1,
};
typedef uint32_t Flags;
// Construct a new reorder instance.
// @param module_path The path of the module dll.
// @param instrumented_path The path of the instrumented dll.
// @param trace_files A list of trace files to analyze.
// @param flags Flags passed to Reorderer.
Reorderer(const base::FilePath& module_path,
const base::FilePath& instrumented_path,
const TraceFileList& trace_files,
Flags flags);
virtual ~Reorderer();
// Runs the reorderer, parsing the call-trace logs and generating an
// ordering using the given order generation strategy.
//
// @note This function cannot be called concurrently across Reorderer
// instances because the ETW parser must be a singleton due to the
// way the Windows ETW API is structured. This is enforced in debug
// builds.
//
// @returns true on success, false otherwise.
// @pre order must not be NULL.
bool Reorder(OrderGenerator* order_generator,
Order* order,
PEFile* pe_file,
ImageLayout* image);
// @name Accessors
// @{
Flags flags() const { return flags_; }
const Parser& parser() const { return parser_; }
// @}
protected:
typedef block_graph::BlockGraph BlockGraph;
typedef core::RelativeAddress RelativeAddress;
typedef playback::Playback Playback;
typedef std::set<uint32_t> ProcessSet;
typedef trace::parser::ModuleInformation ModuleInformation;
typedef TraceFileList::iterator TraceFileIter;
// The implementation of Reorder.
bool ReorderImpl(Order* order, PEFile* pe_file, ImageLayout* image);
// Calculates the actual reordering.
bool CalculateReordering(Order* order);
// @name ParseEventHandler overrides.
// @{
void OnProcessStarted(base::Time time,
DWORD process_id,
const TraceSystemInfo* data) override;
void OnProcessEnded(base::Time time, DWORD process_id) override;
void OnFunctionEntry(base::Time time,
DWORD process_id,
DWORD thread_id,
const TraceEnterExitEventData* data) override;
void OnBatchFunctionEntry(base::Time time,
DWORD process_id,
DWORD thread_id,
const TraceBatchEnterData* data) override;
// @}
// A playback, which will decompose the image for us.
Playback playback_;
// A set of flags controlling the reorderer's behaviour.
Flags flags_;
// Number of CodeBlockEntry events processed.
size_t code_block_entry_events_;
// The following three variables are only valid while Reorder is executing.
// A pointer to our order generator delegate.
OrderGenerator* order_generator_;
// The call-trace log file parser. It is used in conjunction with Playback
// to trace the log file and capture events.
Parser parser_;
// A cache for whether or not to reorder each section.
typedef std::vector<bool> SectionReorderabilityCache;
SectionReorderabilityCache section_reorderability_cache_;
DISALLOW_COPY_AND_ASSIGN(Reorderer);
};
// An ordering consists of a series of section specifications, where each
// section specification describes the section (allowing it to be identified
// in the original image or created it it does not already exist) and the
// list of blocks that go into that section.
//
// Each block denotes the source block plus an optional ordered set of RVAs
// falling within said block which denotes the basic blocks to include.
//
// TODO(rogerm): Flesh out support for synthesized blocks. In this scenario,
// the referenced source block would be NULL and the basic-blocks RVAs
// included in the synthesized block would be allowed to come from anywhere
// in the image.
//
// An order may be serialized to and from JSON, in the following format:
//
// {
// 'metadata': {
// // This contains tool-chain information, command-line info, etc.
// },
// 'sections': [
// {
// "id": 1,
// "name": ".foo",
// "characteristics": 11111,
// "blocks": [
// 22222, # Use this block in it's entirety.
// 33333, # Ditto.
// [ 44444, [ 44444, 44448, .... ]], // Use selected basic-blocks.
// ...
// ]
// },
// ...
// ]
// }
//
// A section may be given either by its id (zero-based index) in the original
// image layout and/or by name and characteristics. If given by id, the name
// and characteristics are optional. These values will be populated from the
// corresponding section in the original image. If the name and characteristics
// are also specified in addition to the id, they will over-ride the values in
// the original image. If the id is not specified, then name and characteristics
// are required and denote a newly created section.
struct Reorderer::Order {
typedef BlockGraph::Block Block;
typedef BlockGraph::Offset Offset;
typedef std::vector<Offset> OffsetVector;
// The structure describing a block in the ordering.
struct BlockSpec {
BlockSpec() : block(NULL) {}
explicit BlockSpec(const Block* b) : block(b) { DCHECK(b != NULL); }
// The source block.
const Block* block;
// The offsets of block's basic-blocks to include when placing this block
// in the reordered image.
OffsetVector basic_block_offsets;
};
// A type denoting an ordered collection of BlockSpec objects.
typedef std::vector<BlockSpec> BlockSpecVector;
// The structure describing a section in the ordering.
struct SectionSpec {
SectionSpec() : id(kNewSectionId), characteristics(0) {
}
// A sentinel section id denoting that a new section should be created.
static const BlockGraph::SectionId kNewSectionId;
// Section ID. If this is kNewSectionId then this specification describes a
// new section to be added to the image. Otherwise, it refers to an existing
// section in the original image. If this is not explicitly specified in the
// JSON file, it defaults to kNewSectionId.
BlockGraph::SectionId id;
// The name this section should have in the final image. If id refers to
// an existing section and the name does not match, then the section will
// be renamed. If this is not explicitly set in the JSON file then the the
// ID must be specified and this well be populated with the section name of
// the original section in the original image layout.
std::string name;
// The characteristics this section should have in the final image. If id
// refers to an existing section and the characteristics do not match, then
// they will be changed. If this is not explicitly set in the JSON file then
// the id must be specified and this will be populated with the section
// characteristics of the original section in in the original image layout.
DWORD characteristics;
// The ordered collection of blocks in this section.
BlockSpecVector blocks;
};
// An ordered collection of section specifications. The order in which the
// section specifications occur in the collection will be the order in
// which the orderer will attempt to construct the output image. Note that
// not all orders may be valid. Each orderer is free to define whether or
// not it will honor the section ordering on a best-effort basis, or will
// reject outright an invalid ordering.
typedef std::vector<SectionSpec> SectionSpecVector;
Order() {}
// A comment describing the ordering.
std::string comment;
// The ordered collection of section specifications.
SectionSpecVector sections;
// Serializes the order to JSON. Returns true on success, false otherwise.
// The serialization simply consists of the start addresses of each block
// in a JSON list. Pretty-printing adds further information from the
// BlockGraph via inline comments.
// @{
bool SerializeToJSON(const PEFile& pe,
const base::FilePath& path,
bool pretty_print) const;
bool SerializeToJSON(const PEFile& pe,
core::JSONFileWriter* json_file) const;
// @}
// Loads an ordering from a JSON file.
// @note @p pe and @p image must already be populated prior to calling this.
bool LoadFromJSON(const PEFile& pe,
const ImageLayout& image,
const base::FilePath& path);
// Extracts the name of the original module from an order file. This is
// used to guess the value of --input-image.
static bool GetOriginalModulePath(const base::FilePath& path,
base::FilePath* module);
private:
DISALLOW_COPY_AND_ASSIGN(Order);
};
// The actual class that does the work, an order generator. It receives
// call trace events (already mapped to blocks in a disassembled image),
// and is asked to build an ordering.
class Reorderer::OrderGenerator {
public:
typedef block_graph::BlockGraph BlockGraph;
typedef BlockGraph::AddressSpace AddressSpace;
typedef core::RelativeAddress RelativeAddress;
typedef pe::ImageLayout ImageLayout;
typedef pe::PEFile PEFile;
typedef Reorderer::Order Order;
typedef Reorderer::UniqueTime UniqueTime;
explicit OrderGenerator(const char* name) : name_(name) {}
virtual ~OrderGenerator() {}
// Accessor.
const std::string& name() const { return name_; }
// The derived class may implement this callback, which indicates when a
// process invoking the instrumented module was started.
virtual bool OnProcessStarted(uint32_t process_id, const UniqueTime& time) {
return true;
}
// The derived class may implement this callback, which provides
// information on the end of processes invoking the instrumented module.
// Processes whose lifespan exceed the logging period will not receive
// OnProcessEnded events.
virtual bool OnProcessEnded(uint32_t process_id, const UniqueTime& time) {
return true;
}
// The derived class shall implement this callback, which receives
// TRACE_ENTRY events for the module that is being reordered. Returns true
// on success, false on error. If this returns false, no further callbacks
// will be processed.
virtual bool OnCodeBlockEntry(const BlockGraph::Block* block,
RelativeAddress address,
uint32_t process_id,
uint32_t thread_id,
const UniqueTime& time) = 0;
// The derived class shall implement this function, which actually produces
// the reordering. When this is called, the callee can be assured that the
// ImageLayout is populated and all traces have been parsed. This must
// return true on success, false otherwise.
virtual bool CalculateReordering(const PEFile& pe_file,
const ImageLayout& image,
bool reorder_code,
bool reorder_data,
Order* order) = 0;
private:
const std::string name_;
DISALLOW_COPY_AND_ASSIGN(OrderGenerator);
};
// A unique time class. No two instances of this class will ever be equal
// This allows events that map to the same time (down to the resolution reported
// to us) to still maintain a unique temporal ordering. This is done by using
// a secondary counter value. It is necessary because we often get buffers full
// of events that have the same time indicated, but that we know to be in the
// temporal order in which they are stored in the buffer.
class Reorderer::UniqueTime {
public:
// This class has a copy-constructor and is assignable in order to be STL
// container compatible.
UniqueTime();
UniqueTime(const UniqueTime& other);
explicit UniqueTime(const base::Time& time);
UniqueTime& operator=(const UniqueTime& rhs);
const base::Time& time() const { return time_; }
size_t id() const { return id_; }
// Compares two UniqueTime objects, returning a value from the set {-1, 0, 1}.
int compare(const UniqueTime& rhs) const;
// Standard comparison operators.
bool operator<(const UniqueTime& rhs) const { return compare(rhs) < 0; }
bool operator>(const UniqueTime& rhs) const { return compare(rhs) > 0; }
bool operator<=(const UniqueTime& rhs) const { return compare(rhs) <= 0; }
bool operator>=(const UniqueTime& rhs) const { return compare(rhs) >= 0; }
bool operator==(const UniqueTime& rhs) const { return compare(rhs) == 0; }
bool operator!=(const UniqueTime& rhs) const { return compare(rhs) != 0; }
private:
base::Time time_;
size_t id_;
// Stores the next id that will be used in constructing a unique time object.
static size_t next_id_;
};
} // namespace reorder
#endif // SYZYGY_REORDER_REORDERER_H_