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 E : bool error = false;
445 : const BlockGraph::Block* block = playback_.FindFunctionBlock(process_id,
446 : data->function,
447 E : &error);
448 :
449 : // Handle the error if any occurred.
450 E : if (error) {
451 i : LOG(ERROR) << "Playback::FindFunctionBlock failed.";
452 i : parser_.set_error_occurred(true);
453 i : return;
454 : }
455 :
456 : // If no block was found then we simply ignore the event.
457 E : if (block == NULL)
458 i : return;
459 :
460 : // Get the time of the call. Since batched function calls come in with the
461 : // same time stamp, we rely on their relative ordering and UniqueTime's
462 : // incrementing ID to maintain relative order.
463 E : UniqueTime entry_time(time);
464 :
465 E : ++code_block_entry_events_;
466 : if (!order_generator_->OnCodeBlockEntry(block,
467 : block->addr(),
468 : process_id,
469 : thread_id,
470 E : entry_time)) {
471 i : LOG(ERROR) << order_generator_->name() << "::OnCodeBlockEntry failed.";
472 i : parser_.set_error_occurred(true);
473 : return;
474 : }
475 E : }
476 :
477 : void Reorderer::OnBatchFunctionEntry(base::Time time,
478 : DWORD process_id,
479 : DWORD thread_id,
480 E : const TraceBatchEnterData* data) {
481 : // Explode the batch event into individual function entry events.
482 E : TraceEnterExitEventData new_data = {};
483 E : for (size_t i = 0; i < data->num_calls; ++i) {
484 E : new_data.function = data->calls[i].function;
485 E : OnFunctionEntry(time, process_id, thread_id, &new_data);
486 E : }
487 E : }
488 :
489 : bool Reorderer::Order::SerializeToJSON(const PEFile& pe,
490 : const base::FilePath &path,
491 E : bool pretty_print) const {
492 E : file_util::ScopedFILE file(file_util::OpenFile(path, "wb"));
493 E : if (file.get() == NULL)
494 i : return false;
495 E : core::JSONFileWriter json_file(file.get(), pretty_print);
496 E : return SerializeToJSON(pe, &json_file);
497 E : }
498 :
499 : bool Reorderer::Order::SerializeToJSON(const PEFile& pe,
500 E : core::JSONFileWriter* json_file) const {
501 E : DCHECK(json_file != NULL);
502 :
503 : // Open the top-level dictionary and the metadata dictionary.
504 E : if (!json_file->OpenDict())
505 i : return false;
506 :
507 : // Output the filecomment.
508 : if (!json_file->OutputKey(kCommentKey) ||
509 E : !json_file->OutputString(comment)) {
510 i : return false;
511 : }
512 :
513 : // Output metadata.
514 E : PEFile::Signature orig_sig;
515 E : pe.GetSignature(&orig_sig);
516 E : pe::Metadata metadata;
517 : if (!metadata.Init(orig_sig) ||
518 : !json_file->OutputKey(kMetadataKey) ||
519 E : !metadata.SaveToJSON(json_file)) {
520 i : return false;
521 : }
522 :
523 : // Open list of sections.
524 : if (!json_file->OutputKey(kSectionsKey) ||
525 E : !json_file->OpenList()) {
526 i : return false;
527 : }
528 :
529 : // Output the individual block lists.
530 E : SectionSpecVector::const_iterator it = sections.begin();
531 E : for (; it != sections.end(); ++it) {
532 E : const SectionSpec& section_spec = *it;
533 E : if (section_spec.blocks.empty())
534 i : continue;
535 :
536 E : if (!OutputSectionSpec(section_spec, json_file))
537 i : return false;
538 E : }
539 :
540 : // Close the list of sections.
541 E : if (!json_file->CloseList())
542 i : return false;
543 :
544 : // Close the outermost dictionary.
545 E : if (!json_file->CloseDict())
546 i : return false;
547 :
548 E : return true;
549 E : }
550 :
551 : bool Reorderer::Order::LoadFromJSON(const PEFile& pe,
552 : const ImageLayout& image,
553 E : const base::FilePath& path) {
554 E : std::string file_string;
555 E : if (!file_util::ReadFileToString(path, &file_string)) {
556 i : LOG(ERROR) << "Unable to read order file to string";
557 i : return false;
558 : }
559 :
560 : // Read in the JSON file. It should be a dictionary.
561 E : const DictionaryValue* outer_dict = NULL;
562 E : scoped_ptr<Value> value(base::JSONReader::Read(file_string));
563 E : if (value.get() == NULL || !value->GetAsDictionary(&outer_dict)) {
564 i : LOG(ERROR) << "Order file does not contain a valid JSON dictionary.";
565 i : return false;
566 : }
567 :
568 : // Load the metadata from the order file, and ensure it is consistent with
569 : // the signature of the module the ordering is being applied to.
570 E : pe::Metadata metadata;
571 E : PEFile::Signature pe_sig;
572 E : pe.GetSignature(&pe_sig);
573 E : const DictionaryValue* metadata_dict = NULL;
574 : if (!outer_dict->GetDictionary(kMetadataKey, &metadata_dict) ||
575 : !metadata.LoadFromJSON(*metadata_dict) ||
576 E : !metadata.IsConsistent(pe_sig)) {
577 i : LOG(ERROR) << "Missing, invalid, or inconsistent " << kMetadataKey << ".";
578 i : return false;
579 : }
580 :
581 : // Load the comments field.
582 : if (outer_dict->HasKey(kCommentKey) &&
583 E : !outer_dict->GetString(kCommentKey, &comment)) {
584 i : LOG(ERROR) << "Invalid " << kCommentKey << " value. Must be a string.";
585 i : return false;
586 : }
587 :
588 : // Grab the sections list.
589 E : const ListValue* order = NULL;
590 E : if (!outer_dict->GetList(kSectionsKey, &order)) {
591 i : LOG(ERROR) << "Missing or invalid " << kSectionsKey << ".";
592 i : return false;
593 : }
594 :
595 : // Allocate the expected number of sections.
596 E : sections.clear();
597 :
598 : // Iterate through the elements of the list. They should each be dictionaries
599 : // representing a single section. We'll also track the sections descriptions
600 : // we have already seen.
601 E : std::set<size_t> seen_section_ids;
602 E : sections.resize(order->GetSize());
603 E : for (size_t index = 0; index < order->GetSize(); ++index) {
604 E : const DictionaryValue* section = NULL;
605 E : if (!order->GetDictionary(index, §ion)) {
606 i : LOG(ERROR) << "Item " << index << "of " << kSectionsKey
607 : << " list is not a dictionary.";
608 i : return false;
609 : }
610 :
611 E : if (!LoadSectionSpec(image, section, §ions[index], &seen_section_ids))
612 i : return false;
613 E : }
614 :
615 E : return true;
616 E : }
617 :
618 : bool Reorderer::Order::GetOriginalModulePath(const base::FilePath& path,
619 E : base::FilePath* module) {
620 E : std::string file_string;
621 E : if (!file_util::ReadFileToString(path, &file_string)) {
622 E : LOG(ERROR) << "Unable to read order file to string.";
623 E : return false;
624 : }
625 :
626 E : scoped_ptr<Value> value(base::JSONReader::Read(file_string));
627 E : if (value.get() == NULL || value->GetType() != Value::TYPE_DICTIONARY) {
628 i : LOG(ERROR) << "Order file does not contain a valid JSON dictionary.";
629 i : return false;
630 : }
631 : const DictionaryValue* outer_dict =
632 E : reinterpret_cast<const DictionaryValue*>(value.get());
633 :
634 E : std::string metadata_key("metadata");
635 E : const DictionaryValue* metadata_dict = NULL;
636 E : if (!outer_dict->GetDictionary(metadata_key, &metadata_dict)) {
637 i : LOG(ERROR) << "Order dictionary must contain 'metadata'.";
638 i : return false;
639 : }
640 :
641 E : pe::Metadata metadata;
642 E : if (!metadata.LoadFromJSON(*metadata_dict))
643 i : return false;
644 :
645 E : *module = base::FilePath(metadata.module_signature().path);
646 :
647 E : return true;
648 E : }
649 :
650 : Reorderer::UniqueTime::UniqueTime()
651 : : time_(),
652 : id_(0) {
653 : }
654 :
655 : Reorderer::UniqueTime::UniqueTime(const UniqueTime& other)
656 : : time_(other.time_),
657 E : id_(other.id_) {
658 E : }
659 :
660 : Reorderer::UniqueTime::UniqueTime(const base::Time& time)
661 : : time_(time),
662 E : id_(next_id_++) {
663 E : }
664 :
665 E : Reorderer::UniqueTime& Reorderer::UniqueTime::operator=(const UniqueTime& rhs) {
666 E : time_ = rhs.time_;
667 E : id_ = rhs.id_;
668 E : return *this;
669 E : }
670 :
671 E : int Reorderer::UniqueTime::compare(const UniqueTime& rhs) const {
672 E : if (time_ < rhs.time_)
673 E : return -1;
674 E : if (time_ > rhs.time_)
675 E : return 1;
676 E : if (id_ < rhs.id_)
677 E : return -1;
678 E : if (id_ > rhs.id_)
679 E : return 1;
680 E : return 0;
681 E : }
682 :
683 : size_t Reorderer::UniqueTime::next_id_ = 0;
684 :
685 : } // namespace reorder
|