1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/reorder/reorderer.h"
16 :
17 : #include "base/file_util.h"
18 : #include "base/stringprintf.h"
19 : #include "base/utf_string_conversions.h"
20 : #include "base/values.h"
21 : #include "base/json/json_reader.h"
22 : #include "base/json/string_escape.h"
23 : #include "syzygy/block_graph/block_graph.h"
24 : #include "syzygy/common/defs.h"
25 : #include "syzygy/common/syzygy_version.h"
26 : #include "syzygy/core/json_file_writer.h"
27 : #include "syzygy/core/serialization.h"
28 : #include "syzygy/pdb/omap.h"
29 : #include "syzygy/pe/find.h"
30 : #include "syzygy/pe/metadata.h"
31 : #include "syzygy/pe/pe_file.h"
32 : #include "syzygy/pe/pe_utils.h"
33 :
34 : namespace reorder {
35 :
36 : using block_graph::BlockGraph;
37 : using trace::parser::Parser;
38 : using base::ListValue;
39 : using base::DictionaryValue;
40 : using base::Value;
41 :
42 : namespace {
43 :
44 : const char kCommentKey[] = "comment";
45 : const char kMetadataKey[] = "metadata";
46 : const char kSectionsKey[] = "sections";
47 : const char kSectionIdKey[] = "id";
48 : const char kSectionNameKey[] = "name";
49 : const char kSectionCharacteristicsKey[] = "characteristics";
50 : const char kBlocksKey[] = "blocks";
51 :
52 : bool OutputTrailingBlockComment(const BlockGraph::Block* block,
53 E : core::JSONFileWriter* json_file) {
54 E : DCHECK(block != NULL);
55 E : DCHECK(json_file != NULL);
56 :
57 E : if (!json_file->pretty_print())
58 E : return true;
59 :
60 : std::string comment = base::StringPrintf(
61 : "%s(%s)",
62 : BlockGraph::BlockTypeToString(block->type()),
63 E : block->name().c_str());
64 :
65 E : if (!json_file->OutputTrailingComment(comment))
66 i : return false;
67 :
68 E : return true;
69 E : }
70 :
71 : bool OutputBlockSpec(const Reorderer::Order::BlockSpec& block_spec,
72 E : core::JSONFileWriter* json_file) {
73 : // TODO(rogerm): Stop referring to block->addr() and take the address space
74 : // as an input parameter.
75 E : DCHECK(json_file != NULL);
76 : // TODO(rogerm): Flesh out support for synthesizing new blocks.
77 E : DCHECK(block_spec.block != NULL);
78 :
79 : // If no basic-block RVAs are given then the entire block is to be
80 : // used and we can just just the block address.
81 E : if (block_spec.basic_block_offsets.empty()) {
82 E : if (!json_file->OutputInteger(block_spec.block->addr().value()))
83 i : return false;
84 E : if (!OutputTrailingBlockComment(block_spec.block, json_file))
85 i : return false;
86 E : return true;
87 : }
88 :
89 : // Otherwise, we output a pair (two element list) comprising the block
90 : // address and the list of basic-block RVAs.
91 :
92 : // Open the outer list.
93 E : if (!json_file->OpenList())
94 i : return false;
95 :
96 : // Output the block address.
97 : if (!json_file->OutputInteger(block_spec.block->addr().value()) ||
98 E : !OutputTrailingBlockComment(block_spec.block, json_file)) {
99 i : return false;
100 : }
101 :
102 : // Open the inner list.
103 E : if (!json_file->OpenList())
104 i : return false;
105 :
106 : // Output the basic block RVAs.
107 : Reorderer::Order::OffsetVector::const_iterator it =
108 E : block_spec.basic_block_offsets.begin();
109 E : for (; it != block_spec.basic_block_offsets.end(); ++it) {
110 E : if (!json_file->OutputInteger(*it))
111 i : return false;
112 E : }
113 :
114 : // Close the inner list.
115 E : if (!json_file->CloseList())
116 i : return false;
117 :
118 : // Close the outer list.
119 E : if (!json_file->CloseList())
120 i : return false;
121 :
122 E : return true;
123 E : }
124 :
125 : // Serializes a block list to JSON.
126 : bool OutputSectionSpec(const Reorderer::Order::SectionSpec& section_spec,
127 E : core::JSONFileWriter* json_file) {
128 E : DCHECK(json_file != NULL);
129 :
130 : // Open the section specification dictionary.
131 E : if (!json_file->OpenDict())
132 i : return false;
133 :
134 : // If the section has an ID in the original image, output the ID.
135 E : if (section_spec.id != Reorderer::Order::SectionSpec::kNewSectionId) {
136 : if (!json_file->OutputKey(kSectionIdKey) ||
137 E : !json_file->OutputInteger(section_spec.id)) {
138 i : return false;
139 : }
140 : }
141 :
142 : // Output the section metadata.
143 : if (!json_file->OutputKey(kSectionNameKey) ||
144 : !json_file->OutputString(section_spec.name) ||
145 : !json_file->OutputKey(kSectionCharacteristicsKey) ||
146 E : !json_file->OutputInteger(section_spec.characteristics)) {
147 i : return false;
148 : }
149 :
150 : // Open the block spec list.
151 : if (!json_file->OutputKey(kBlocksKey) ||
152 E : !json_file->OpenList()) {
153 i : return false;
154 : }
155 :
156 : // Output each of the block specifications.
157 : Reorderer::Order::BlockSpecVector::const_iterator it =
158 E : section_spec.blocks.begin();
159 E : for (; it != section_spec.blocks.end(); ++it) {
160 E : if (!OutputBlockSpec(*it, json_file))
161 i : return false;
162 E : }
163 :
164 : // Close the block spec list.
165 E : if (!json_file->CloseList())
166 i : return false;
167 :
168 : // Close the section spec dictionary.
169 E : if (!json_file->CloseDict())
170 i : return false;
171 :
172 E : return true;
173 E : }
174 :
175 : bool LoadBlockSpec(const pe::ImageLayout& image,
176 : const Value* block_value,
177 E : Reorderer::Order::BlockSpec* block_spec) {
178 E : DCHECK(block_value != NULL);
179 E : DCHECK(block_spec != NULL);
180 :
181 E : block_spec->block = NULL;
182 E : block_spec->basic_block_offsets.clear();
183 :
184 : // If the block value is a single integer, then we use the entire block.
185 : // Otherwise, we evaluate the value as a pair (represented as a list)
186 : // where the first element is the address and the second element is a list
187 : // of basic-block RVAs.
188 E : int address = 0;
189 E : const ListValue* rva_list = NULL;
190 E : if (!block_value->GetAsInteger(&address)) {
191 E : const ListValue* pair = NULL;
192 : if (!block_value->GetAsList(&pair) ||
193 : !pair->GetInteger(0, &address) ||
194 E : !pair->GetList(1, &rva_list)) {
195 i : LOG(ERROR) << "Invalid entry for block specification.";
196 i : return false;
197 : }
198 : }
199 :
200 : // Resolve the referenced block.
201 E : core::RelativeAddress rva(address);
202 E : const BlockGraph::Block* block = image.blocks.GetBlockByAddress(rva);
203 E : if (block == NULL) {
204 i : LOG(ERROR) << "Block address not found in decomposed image: "
205 : << address;
206 i : return false;
207 : }
208 :
209 : // Read in the basic_block offsets.
210 E : if (rva_list != NULL && !rva_list->empty()) {
211 E : block_spec->basic_block_offsets.reserve(rva_list->GetSize());
212 E : for (size_t i = 0; i < rva_list->GetSize(); ++i) {
213 E : int offset = 0;
214 E : if (!rva_list->GetInteger(i, &offset)) {
215 i : LOG(ERROR) << "Unexpected value for basic-block offset #" << i
216 : << " of " << address << " [" << block->name() << "].";
217 i : block_spec->basic_block_offsets.clear();
218 i : return false;
219 : }
220 : if (offset < 0 ||
221 E : static_cast<BlockGraph::Size>(offset) >= block->size()) {
222 i : LOG(ERROR) << "Offset " << offset << " falls outside block range [0-"
223 : << (block->size() - 1) << "] for " << block->name();
224 i : return false;
225 : }
226 E : block_spec->basic_block_offsets.push_back(offset);
227 E : }
228 : }
229 :
230 E : block_spec->block = block;
231 E : return true;
232 E : }
233 :
234 : bool LoadSectionSpec(const pe::ImageLayout& image,
235 : const DictionaryValue* section_value,
236 : Reorderer::Order::SectionSpec* section_spec,
237 E : std::set<size_t>* seen_section_ids) {
238 E : DCHECK(section_value != NULL);
239 E : DCHECK(section_spec != NULL);
240 E : DCHECK(seen_section_ids != NULL);
241 :
242 : // Some keys we'll refer to multiple times below.
243 E : const std::string section_id_key(kSectionIdKey);
244 E : const std::string section_name_key(kSectionNameKey);
245 E : const std::string section_characteristics_key(kSectionCharacteristicsKey);
246 E : const std::string blocks_key(kBlocksKey);
247 :
248 : // Get the section id, if given.
249 E : int tmp_section_id = Reorderer::Order::SectionSpec::kNewSectionId;
250 : if (section_value->HasKey(section_id_key) &&
251 E : !section_value->GetInteger(section_id_key, &tmp_section_id)) {
252 i : LOG(ERROR) << "Invalid value for " << section_id_key << ".";
253 i : return false;
254 : }
255 :
256 : // Lookup the original section by id, if the id was given. Populate the
257 : // section metadata based on the original section info. The other keys
258 : // will be inspected below to see if any of the metadata needs to be
259 : // over-ridden.
260 E : section_spec->id = tmp_section_id;
261 E : if (section_spec->id != Reorderer::Order::SectionSpec::kNewSectionId) {
262 : // Lookup the section in the original image layout.
263 E : if (section_spec->id < 0 || section_spec->id > image.sections.size()) {
264 i : LOG(ERROR) << "Invalid section id: " << section_spec->id << ".";
265 i : return false;
266 : }
267 :
268 : // Make sure this section id does not already exist.
269 E : if (!seen_section_ids->insert(section_spec->id).second) {
270 i : LOG(ERROR) << "Section ID " << section_spec->id << " redefined.";
271 i : return false;
272 : }
273 :
274 : // Copy the metadata into the section spec.
275 E : section_spec->name = image.sections[section_spec->id].name;
276 : section_spec->characteristics =
277 E : image.sections[section_spec->id].characteristics;
278 : }
279 :
280 : // Possibly over-ride the section name.
281 : if (section_value->HasKey(section_name_key) &&
282 E : !section_value->GetString(section_name_key, §ion_spec->name)) {
283 i : LOG(ERROR) << "Invalid value for " << section_name_key << ".";
284 i : return false;
285 : }
286 :
287 : // Make sure we've got a section name. This may have come from either the
288 : // the original image (via the section id) or explicitly from the section
289 : // name key.
290 E : if (section_spec->name.empty()) {
291 i : LOG(ERROR) << "Missing a value for the section name. Either a valid "
292 : << section_id_key << " or valid " << section_name_key
293 : << " is required.";
294 i : return false;
295 : }
296 :
297 : // Possibly over-ride the section characteristics. The characteristics are
298 : // required if the section was not given by id.
299 : if (section_spec->id == Reorderer::Order::SectionSpec::kNewSectionId ||
300 E : section_value->HasKey(section_characteristics_key)) {
301 E : int tmp_characteristics = 0;
302 : if (!section_value->GetInteger(section_characteristics_key,
303 E : &tmp_characteristics)) {
304 i : LOG(ERROR) << "Missing or invalid value for "
305 : << section_characteristics_key << ".";
306 i : return false;
307 : }
308 E : section_spec->characteristics = tmp_characteristics;
309 : }
310 :
311 : // Get the list of block specifications.
312 E : const ListValue* blocks = NULL;
313 E : if (!section_value->GetList(blocks_key, &blocks)) {
314 i : LOG(ERROR) << "Invalid or missing value for " << blocks_key << ".";
315 i : return false;
316 : }
317 :
318 E : if (!blocks->empty()) {
319 : // Populate the block spec vector.
320 E : section_spec->blocks.resize(blocks->GetSize());
321 E : for (size_t block_idx = 0; block_idx != blocks->GetSize(); ++block_idx) {
322 E : const Value* block_value = NULL;
323 E : if (!blocks->Get(block_idx, &block_value)) {
324 i : LOG(ERROR) << "Failed to access item " << block_idx << ".";
325 i : return false;
326 : }
327 :
328 E : if (!LoadBlockSpec(image, block_value, §ion_spec->blocks[block_idx]))
329 i : return false;
330 E : }
331 : }
332 :
333 E : return true;
334 E : }
335 :
336 : } // namespace
337 :
338 : const size_t Reorderer::Order::SectionSpec::kNewSectionId = ~1;
339 :
340 : Reorderer::Reorderer(const base::FilePath& module_path,
341 : const base::FilePath& instrumented_path,
342 : const TraceFileList& trace_files,
343 : Flags flags)
344 : : playback_(module_path, instrumented_path, trace_files),
345 : flags_(flags),
346 : code_block_entry_events_(0),
347 E : order_generator_(NULL) {
348 E : }
349 :
350 E : Reorderer::~Reorderer() {
351 E : }
352 :
353 : bool Reorderer::Reorder(OrderGenerator* order_generator,
354 : Order* order,
355 : PEFile* pe_file,
356 E : ImageLayout* image) {
357 E : DCHECK(order_generator != NULL);
358 E : DCHECK(order != NULL);
359 :
360 E : DCHECK(order_generator_ == NULL);
361 E : order_generator_ = order_generator;
362 :
363 E : bool success = ReorderImpl(order, pe_file, image);
364 :
365 E : order_generator_ = NULL;
366 :
367 E : return success;
368 E : }
369 :
370 : bool Reorderer::ReorderImpl(Order* order,
371 : PEFile* pe_file,
372 E : ImageLayout* image) {
373 E : DCHECK(order != NULL);
374 E : DCHECK(order_generator_ != NULL);
375 :
376 E : if (!parser_.Init(this)) {
377 i : LOG(ERROR) << "Failed to initialize call trace parser.";
378 i : return false;
379 : }
380 :
381 E : if (!playback_.Init(pe_file, image, &parser_))
382 i : return false;
383 :
384 E : if (playback_.trace_files().size() > 0) {
385 E : LOG(INFO) << "Processing trace events.";
386 E : if (!parser_.Consume())
387 i : return false;
388 :
389 E : if (code_block_entry_events_ == 0) {
390 i : LOG(ERROR) << "No events originated from the given instrumented DLL.";
391 i : return false;
392 : }
393 : }
394 :
395 E : if (!CalculateReordering(order))
396 i : return false;
397 :
398 E : return true;
399 E : }
400 :
401 E : bool Reorderer::CalculateReordering(Order* order) {
402 E : DCHECK(order != NULL);
403 E : DCHECK(order_generator_ != NULL);
404 :
405 E : LOG(INFO) << "Calculating new order.";
406 : if (!order_generator_->CalculateReordering(*playback_.pe_file(),
407 : *playback_.image(),
408 : (flags_ & kFlagReorderCode) != 0,
409 : (flags_ & kFlagReorderData) != 0,
410 E : order))
411 i : return false;
412 :
413 : order->comment = base::StringPrintf("Generated using the %s.",
414 E : order_generator_->name().c_str());
415 :
416 E : return true;
417 E : }
418 :
419 : void Reorderer::OnProcessStarted(
420 E : base::Time time, DWORD process_id, const TraceSystemInfo* data) {
421 E : UniqueTime entry_time(time);
422 :
423 E : if (!order_generator_->OnProcessStarted(process_id, entry_time)) {
424 i : parser_.set_error_occurred(true);
425 : return;
426 : }
427 E : }
428 :
429 E : void Reorderer::OnProcessEnded(base::Time time, DWORD process_id) {
430 : // Notify the order generator.
431 E : if (!order_generator_->OnProcessEnded(process_id, UniqueTime(time))) {
432 i : parser_.set_error_occurred(true);
433 : return;
434 : }
435 E : }
436 :
437 : // CallTraceEvents implementation.
438 : void Reorderer::OnFunctionEntry(base::Time time,
439 : DWORD process_id,
440 : DWORD thread_id,
441 E : const TraceEnterExitEventData* data) {
442 E : DCHECK(data != NULL);
443 :
444 : const BlockGraph::Block* block = playback_.FindFunctionBlock(process_id,
445 E : data->function);
446 :
447 E : if (block == NULL) {
448 i : parser_.set_error_occurred(true);
449 i : return;
450 : }
451 :
452 : // Get the time of the call. Since batched function calls come in with the
453 : // same time stamp, we rely on their relative ordering and UniqueTime's
454 : // incrementing ID to maintain relative order.
455 E : UniqueTime entry_time(time);
456 :
457 E : ++code_block_entry_events_;
458 : if (!order_generator_->OnCodeBlockEntry(block,
459 : block->addr(),
460 : process_id,
461 : thread_id,
462 E : entry_time)) {
463 i : parser_.set_error_occurred(true);
464 : return;
465 : }
466 E : }
467 :
468 : void Reorderer::OnBatchFunctionEntry(base::Time time,
469 : DWORD process_id,
470 : DWORD thread_id,
471 E : const TraceBatchEnterData* data) {
472 : // Explode the batch event into individual function entry events.
473 E : TraceEnterExitEventData new_data = {};
474 E : for (size_t i = 0; i < data->num_calls; ++i) {
475 E : new_data.function = data->calls[i].function;
476 E : OnFunctionEntry(time, process_id, thread_id, &new_data);
477 E : }
478 E : }
479 :
480 : bool Reorderer::Order::SerializeToJSON(const PEFile& pe,
481 : const base::FilePath &path,
482 E : bool pretty_print) const {
483 E : file_util::ScopedFILE file(file_util::OpenFile(path, "wb"));
484 E : if (file.get() == NULL)
485 i : return false;
486 E : core::JSONFileWriter json_file(file.get(), pretty_print);
487 E : return SerializeToJSON(pe, &json_file);
488 E : }
489 :
490 : bool Reorderer::Order::SerializeToJSON(const PEFile& pe,
491 E : core::JSONFileWriter* json_file) const {
492 E : DCHECK(json_file != NULL);
493 :
494 : // Open the top-level dictionary and the metadata dictionary.
495 E : if (!json_file->OpenDict())
496 i : return false;
497 :
498 : // Output the filecomment.
499 : if (!json_file->OutputKey(kCommentKey) ||
500 E : !json_file->OutputString(comment)) {
501 i : return false;
502 : }
503 :
504 : // Output metadata.
505 E : PEFile::Signature orig_sig;
506 E : pe.GetSignature(&orig_sig);
507 E : pe::Metadata metadata;
508 : if (!metadata.Init(orig_sig) ||
509 : !json_file->OutputKey(kMetadataKey) ||
510 E : !metadata.SaveToJSON(json_file)) {
511 i : return false;
512 : }
513 :
514 : // Open list of sections.
515 : if (!json_file->OutputKey(kSectionsKey) ||
516 E : !json_file->OpenList()) {
517 i : return false;
518 : }
519 :
520 : // Output the individual block lists.
521 E : SectionSpecVector::const_iterator it = sections.begin();
522 E : for (; it != sections.end(); ++it) {
523 E : const SectionSpec& section_spec = *it;
524 E : if (section_spec.blocks.empty())
525 i : continue;
526 :
527 E : if (!OutputSectionSpec(section_spec, json_file))
528 i : return false;
529 E : }
530 :
531 : // Close the list of sections.
532 E : if (!json_file->CloseList())
533 i : return false;
534 :
535 : // Close the outermost dictionary.
536 E : if (!json_file->CloseDict())
537 i : return false;
538 :
539 E : return true;
540 E : }
541 :
542 : bool Reorderer::Order::LoadFromJSON(const PEFile& pe,
543 : const ImageLayout& image,
544 E : const base::FilePath& path) {
545 E : std::string file_string;
546 E : if (!file_util::ReadFileToString(path, &file_string)) {
547 i : LOG(ERROR) << "Unable to read order file to string";
548 i : return false;
549 : }
550 :
551 : // Read in the JSON file. It should be a dictionary.
552 E : const DictionaryValue* outer_dict = NULL;
553 E : scoped_ptr<Value> value(base::JSONReader::Read(file_string));
554 E : if (value.get() == NULL || !value->GetAsDictionary(&outer_dict)) {
555 i : LOG(ERROR) << "Order file does not contain a valid JSON dictionary.";
556 i : return false;
557 : }
558 :
559 : // Load the metadata from the order file, and ensure it is consistent with
560 : // the signature of the module the ordering is being applied to.
561 E : pe::Metadata metadata;
562 E : PEFile::Signature pe_sig;
563 E : pe.GetSignature(&pe_sig);
564 E : const DictionaryValue* metadata_dict = NULL;
565 : if (!outer_dict->GetDictionary(kMetadataKey, &metadata_dict) ||
566 : !metadata.LoadFromJSON(*metadata_dict) ||
567 E : !metadata.IsConsistent(pe_sig)) {
568 i : LOG(ERROR) << "Missing, invalid, or inconsistent " << kMetadataKey << ".";
569 i : return false;
570 : }
571 :
572 : // Load the comments field.
573 : if (outer_dict->HasKey(kCommentKey) &&
574 E : !outer_dict->GetString(kCommentKey, &comment)) {
575 i : LOG(ERROR) << "Invalid " << kCommentKey << " value. Must be a string.";
576 i : return false;
577 : }
578 :
579 : // Grab the sections list.
580 E : const ListValue* order = NULL;
581 E : if (!outer_dict->GetList(kSectionsKey, &order)) {
582 i : LOG(ERROR) << "Missing or invalid " << kSectionsKey << ".";
583 i : return false;
584 : }
585 :
586 : // Allocate the expected number of sections.
587 E : sections.clear();
588 :
589 : // Iterate through the elements of the list. They should each be dictionaries
590 : // representing a single section. We'll also track the sections descriptions
591 : // we have already seen.
592 E : std::set<size_t> seen_section_ids;
593 E : sections.resize(order->GetSize());
594 E : for (size_t index = 0; index < order->GetSize(); ++index) {
595 E : const DictionaryValue* section = NULL;
596 E : if (!order->GetDictionary(index, §ion)) {
597 i : LOG(ERROR) << "Item " << index << "of " << kSectionsKey
598 : << " list is not a dictionary.";
599 i : return false;
600 : }
601 :
602 E : if (!LoadSectionSpec(image, section, §ions[index], &seen_section_ids))
603 i : return false;
604 E : }
605 :
606 E : return true;
607 E : }
608 :
609 : bool Reorderer::Order::GetOriginalModulePath(const base::FilePath& path,
610 E : base::FilePath* module) {
611 E : std::string file_string;
612 E : if (!file_util::ReadFileToString(path, &file_string)) {
613 E : LOG(ERROR) << "Unable to read order file to string.";
614 E : return false;
615 : }
616 :
617 E : scoped_ptr<Value> value(base::JSONReader::Read(file_string));
618 E : if (value.get() == NULL || value->GetType() != Value::TYPE_DICTIONARY) {
619 i : LOG(ERROR) << "Order file does not contain a valid JSON dictionary.";
620 i : return false;
621 : }
622 : const DictionaryValue* outer_dict =
623 E : reinterpret_cast<const DictionaryValue*>(value.get());
624 :
625 E : std::string metadata_key("metadata");
626 E : const DictionaryValue* metadata_dict = NULL;
627 E : if (!outer_dict->GetDictionary(metadata_key, &metadata_dict)) {
628 i : LOG(ERROR) << "Order dictionary must contain 'metadata'.";
629 i : return false;
630 : }
631 :
632 E : pe::Metadata metadata;
633 E : if (!metadata.LoadFromJSON(*metadata_dict))
634 i : return false;
635 :
636 E : *module = base::FilePath(metadata.module_signature().path);
637 :
638 E : return true;
639 E : }
640 :
641 : Reorderer::UniqueTime::UniqueTime()
642 : : time_(),
643 : id_(0) {
644 : }
645 :
646 : Reorderer::UniqueTime::UniqueTime(const UniqueTime& other)
647 : : time_(other.time_),
648 E : id_(other.id_) {
649 E : }
650 :
651 : Reorderer::UniqueTime::UniqueTime(const base::Time& time)
652 : : time_(time),
653 E : id_(next_id_++) {
654 E : }
655 :
656 E : Reorderer::UniqueTime& Reorderer::UniqueTime::operator=(const UniqueTime& rhs) {
657 E : time_ = rhs.time_;
658 E : id_ = rhs.id_;
659 E : return *this;
660 E : }
661 :
662 E : int Reorderer::UniqueTime::compare(const UniqueTime& rhs) const {
663 E : if (time_ < rhs.time_)
664 i : return -1;
665 E : if (time_ > rhs.time_)
666 i : return 1;
667 E : if (id_ < rhs.id_)
668 E : return -1;
669 E : if (id_ > rhs.id_)
670 E : return 1;
671 E : return 0;
672 E : }
673 :
674 : size_t Reorderer::UniqueTime::next_id_ = 0;
675 :
676 : } // namespace reorder
|