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 : // Base class for common trace parsing infrastructure.
16 : #include "syzygy/trace/parse/parse_engine.h"
17 :
18 : #include <windows.h> // NOLINT
19 : #include <wmistr.h> // NOLINT
20 : #include <evntrace.h>
21 :
22 : #include "base/logging.h"
23 : #include "syzygy/common/buffer_parser.h"
24 : #include "syzygy/common/com_utils.h"
25 : #include "syzygy/trace/parse/parser.h"
26 :
27 : namespace trace {
28 : namespace parser {
29 :
30 : using ::common::BinaryBufferReader;
31 :
32 : ParseEngine::ParseEngine(const char* name, bool fail_on_module_conflict)
33 E : : event_handler_(nullptr),
34 E : error_occurred_(false),
35 E : fail_on_module_conflict_(fail_on_module_conflict) {
36 E : DCHECK(name != nullptr);
37 E : DCHECK(name[0] != '\0');
38 E : name_ = name;
39 E : }
40 :
41 E : ParseEngine::~ParseEngine() {
42 E : }
43 :
44 E : const char* ParseEngine::name() const {
45 E : return name_.c_str();
46 E : }
47 :
48 E : bool ParseEngine::error_occurred() const {
49 E : return error_occurred_;
50 E : }
51 :
52 E : void ParseEngine::set_error_occurred(bool value) {
53 E : error_occurred_ = value;
54 E : }
55 :
56 E : void ParseEngine::set_event_handler(ParseEventHandler* event_handler) {
57 E : DCHECK(event_handler_ == nullptr);
58 E : DCHECK(event_handler != nullptr);
59 E : event_handler_ = event_handler;
60 E : }
61 :
62 : const ModuleInformation* ParseEngine::GetModuleInformation(
63 : uint32_t process_id,
64 E : AbsoluteAddress64 addr) const {
65 E : ProcessMap::const_iterator processes_it = processes_.find(process_id);
66 E : if (processes_it == processes_.end())
67 E : return nullptr;
68 :
69 E : const ModuleSpace& module_space = processes_it->second;
70 E : ModuleSpace::Range range(addr, 1);
71 : ModuleSpace::RangeMapConstIter module_it =
72 E : module_space.FindFirstIntersection(range);
73 E : if (module_it == module_space.end())
74 E : return nullptr;
75 :
76 E : return &module_it->second;
77 E : }
78 :
79 : bool ParseEngine::AddModuleInformation(DWORD process_id,
80 E : const ModuleInformation& module_info) {
81 : // Avoid doing needless work.
82 E : if (module_info.module_size == 0)
83 i : return true;
84 :
85 : // This happens in Windows XP ETW traces for some reason. They contain
86 : // conflicting information, so we ignore them.
87 E : if (module_info.path.empty())
88 i : return true;
89 :
90 E : ModuleSpace& module_space = processes_[process_id];
91 E : AbsoluteAddress64 addr(module_info.base_address.value());
92 E : ModuleSpace::Range range(addr, module_info.module_size);
93 :
94 E : AnnotatedModuleInformation new_module_info(module_info);
95 :
96 E : ModuleSpace::RangeMapIter iter;
97 E : if (module_space.FindOrInsert(range, new_module_info, &iter)) {
98 E : return true;
99 : }
100 :
101 : // Perhaps this is a case of conflicting paths for the same module. We often
102 : // get paths reported to us in \Device\HarddiskVolumeN\... notation, and
103 : // othertimes in C:\... notation. In this case we're happy if everything
104 : // matches except the path. For a little bit of extra sanity checking we
105 : // also check the basename of the paths.
106 : if (module_info.base_address == iter->second.base_address &&
107 : module_info.module_checksum == iter->second.module_checksum &&
108 E : module_info.module_size == iter->second.module_size &&
109 : module_info.module_time_date_stamp ==
110 : iter->second.module_time_date_stamp) {
111 E : base::FilePath path1(module_info.path);
112 E : base::FilePath path2(iter->second.path);
113 E : if (path1.BaseName() == path2.BaseName()) {
114 E : return true;
115 : }
116 i : }
117 :
118 : // Perhaps this is a case of process id reuse. In that case, we should have
119 : // previously seen a module unload event and marked the module information
120 : // as dirty.
121 E : while (iter->second.is_dirty) {
122 E : module_space.Remove(iter->first);
123 E : if (module_space.FindOrInsert(range, new_module_info, &iter)) {
124 E : return true;
125 : }
126 i : }
127 :
128 E : LOG(ERROR) << "Conflicting module info for pid=" << process_id << ": "
129 : << module_info.path
130 : << " (base=0x" << module_info.base_address
131 : << ", size=" << module_info.module_size << ") and "
132 : << iter->second.path
133 : << " (base=0x" << iter->second.base_address
134 : << ", size=" << iter->second.module_size << ").";
135 :
136 E : return fail_on_module_conflict_ ? false : true;
137 E : }
138 :
139 : bool ParseEngine::RemoveModuleInformation(
140 E : DWORD process_id, const ModuleInformation& module_info) {
141 : // Avoid doing needless work.
142 E : if (module_info.module_size == 0)
143 i : return true;
144 :
145 : // This happens in Windows XP traces for some reason. They contain conflicing
146 : // information, so we ignore them.
147 E : if (module_info.path.empty())
148 i : return true;
149 :
150 E : ModuleSpace& module_space = processes_[process_id];
151 E : AbsoluteAddress64 addr(module_info.base_address.value());
152 E : ModuleSpace::Range range(addr, module_info.module_size);
153 E : ModuleSpace::RangeMapIter it = module_space.FindFirstIntersection(range);
154 E : if (it == module_space.end()) {
155 : // We occasionally see this, as certain modules fire off multiple Unload
156 : // events, so we don't log an error. I'm looking at you, logman.exe.
157 E : return true;
158 : }
159 E : if (it->first != range) {
160 i : LOG(ERROR) << "Trying to remove module with mismatching range: "
161 : << module_info.path
162 : << " (base=0x" << module_info.base_address
163 : << ", size=" << module_info.module_size << ").";
164 i : if (fail_on_module_conflict_)
165 i : return false;
166 : }
167 :
168 : // We only remove modules from a given process if a conflicting module is
169 : // loaded after the module has been marked as dirty. This is because (1) we
170 : // don't guarantee temporal order of all events in a process, so you
171 : // might parse a function event after seeing the module get unloaded
172 : // if the buffers are flushed in that order; and (2) because process ids may
173 : // be reused (but not concurrently) so we do want to drop stale module info
174 : // when the process has been replaced.
175 :
176 E : it->second.is_dirty = true;
177 :
178 E : return true;
179 E : }
180 :
181 E : bool ParseEngine::RemoveProcessInformation(DWORD process_id) {
182 E : ProcessMap::iterator proc_iter = processes_.find(process_id);
183 E : if (proc_iter == processes_.end()) {
184 i : LOG(ERROR) << "Unknown process id: " << process_id << ".";
185 i : return false;
186 : }
187 :
188 E : ModuleSpace& process_info = proc_iter->second;
189 :
190 E : ModuleSpace::iterator module_iter = process_info.begin();
191 E : for (; module_iter != process_info.end(); ++module_iter) {
192 E : module_iter->second.is_dirty = true;
193 E : }
194 :
195 E : return true;
196 E : }
197 :
198 E : bool ParseEngine::DispatchEvent(EVENT_TRACE* event) {
199 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
200 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
201 E : DCHECK(!error_occurred_);
202 :
203 E : if (kCallTraceEventClass != event->Header.Guid)
204 E : return false;
205 :
206 E : bool success = false;
207 E : TraceEventType type = static_cast<TraceEventType>(event->Header.Class.Type);
208 :
209 E : switch (type) {
210 : case TRACE_ENTER_EVENT:
211 : case TRACE_EXIT_EVENT:
212 E : success = DispatchEntryExitEvent(event, type);
213 E : break;
214 :
215 : case TRACE_BATCH_ENTER:
216 E : success = DispatchBatchEnterEvent(event);
217 E : break;
218 :
219 : case TRACE_PROCESS_ATTACH_EVENT:
220 : case TRACE_PROCESS_DETACH_EVENT:
221 : case TRACE_THREAD_ATTACH_EVENT:
222 : case TRACE_THREAD_DETACH_EVENT:
223 E : success = DispatchModuleEvent(event, type);
224 E : break;
225 :
226 : case TRACE_PROCESS_ENDED:
227 E : success = DispatchProcessEndedEvent(event);
228 E : break;
229 :
230 : case TRACE_MODULE_EVENT:
231 i : LOG(ERROR) << "Parsing for TRACE_MODULE_EVENT not yet implemented.";
232 i : break;
233 :
234 : case TRACE_BATCH_INVOCATION:
235 E : success = DispatchBatchInvocationEvent(event);
236 E : break;
237 :
238 : case TRACE_THREAD_NAME:
239 E : success = DispatchThreadNameEvent(event);
240 E : break;
241 :
242 : case TRACE_INDEXED_FREQUENCY:
243 E : success = DispatchIndexedFrequencyEvent(event);
244 E : break;
245 :
246 : case TRACE_DYNAMIC_SYMBOL:
247 E : success = DispatchDynamicSymbolEvent(event);
248 E : break;
249 :
250 : case TRACE_SAMPLE_DATA:
251 E : success = DispatchSampleDataEvent(event);
252 E : break;
253 :
254 : case TRACE_FUNCTION_NAME_TABLE_ENTRY:
255 E : success = DispatchFunctionNameTableEntryEvent(event);
256 E : break;
257 :
258 : case TRACE_STACK_TRACE:
259 E : success = DispatchStackTrace(event);
260 E : break;
261 :
262 : case TRACE_DETAILED_FUNCTION_CALL:
263 E : success = DispatchDetailedFunctionCall(event);
264 E : break;
265 :
266 : case TRACE_COMMENT:
267 E : success = DispatchComment(event);
268 E : break;
269 :
270 : case TRACE_PROCESS_HEAP:
271 E : success = DispatchProcessHeap(event);
272 E : break;
273 :
274 : default:
275 E : LOG(ERROR) << "Unknown event type encountered.";
276 : break;
277 : }
278 :
279 E : if (!success)
280 E : error_occurred_ = true;
281 :
282 E : return true;
283 E : }
284 :
285 : bool ParseEngine::DispatchEntryExitEvent(EVENT_TRACE* event,
286 E : TraceEventType type) {
287 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
288 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
289 E : DCHECK(!error_occurred_);
290 E : DCHECK(type == TRACE_ENTER_EVENT || type == TRACE_EXIT_EVENT);
291 :
292 E : BinaryBufferReader reader(event->MofData, event->MofLength);
293 E : const TraceEnterExitEventData* data = nullptr;
294 :
295 E : if (!reader.Read(sizeof(TraceEnterExitEventData), &data)) {
296 E : LOG(ERROR) << "Short entry exit event.";
297 E : return false;
298 : }
299 :
300 E : base::Time time(base::Time::FromFileTime(
301 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
302 E : DWORD process_id = event->Header.ProcessId;
303 E : DWORD thread_id = event->Header.ThreadId;
304 :
305 E : switch (type) {
306 : case TRACE_ENTER_EVENT:
307 E : event_handler_->OnFunctionEntry(time, process_id, thread_id, data);
308 E : break;
309 :
310 : case TRACE_EXIT_EVENT:
311 E : event_handler_->OnFunctionExit(time, process_id, thread_id, data);
312 E : break;
313 :
314 : default:
315 i : NOTREACHED() << "Impossible event type.";
316 i : return false;
317 : }
318 :
319 E : return true;
320 E : }
321 :
322 E : bool ParseEngine::DispatchBatchEnterEvent(EVENT_TRACE* event) {
323 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
324 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
325 E : DCHECK(!error_occurred_);
326 :
327 E : BinaryBufferReader reader(event->MofData, event->MofLength);
328 E : const TraceBatchEnterData* data = nullptr;
329 E : size_t offset_to_calls = FIELD_OFFSET(TraceBatchEnterData, calls);
330 E : if (!reader.Read(offset_to_calls, &data)) {
331 E : LOG(ERROR) << "Short or empty batch event.";
332 E : return false;
333 : }
334 :
335 E : size_t bytes_needed = data->num_calls * sizeof(data->calls[0]);
336 E : if (!reader.Consume(bytes_needed)) {
337 E : LOG(ERROR) << "Short batch event data. Expected " << data->num_calls
338 : << " entries (" << (offset_to_calls + bytes_needed)
339 : << " bytes) but batch record was only " << event->MofLength
340 : << " bytes.";
341 E : return false;
342 : }
343 :
344 : // Trim the batch entries if the last one is nullptr, indicating that the
345 : // reporting thread was interrupted mid-write.
346 E : if (data->num_calls != 0 &&
347 : data->calls[data->num_calls - 1].function == nullptr) {
348 : // Yuck! Cast away constness because the BinaryBufferReader only likes
349 : // to deal with const output pointers.
350 E : const_cast<TraceBatchEnterData*>(data)->num_calls -= 1;
351 : }
352 E : DCHECK(data->num_calls == 0 ||
353 : data->calls[data->num_calls - 1].function != nullptr);
354 :
355 E : base::Time time(base::Time::FromFileTime(
356 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
357 E : DWORD process_id = event->Header.ProcessId;
358 E : DWORD thread_id = data->thread_id;
359 E : event_handler_->OnBatchFunctionEntry(time, process_id, thread_id, data);
360 E : return true;
361 E : }
362 :
363 E : bool ParseEngine::DispatchProcessEndedEvent(EVENT_TRACE* event) {
364 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
365 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
366 E : DCHECK(!error_occurred_);
367 :
368 E : base::Time time(base::Time::FromFileTime(
369 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
370 :
371 E : event_handler_->OnProcessEnded(time, event->Header.ProcessId);
372 E : if (!RemoveProcessInformation(event->Header.ProcessId))
373 i : return false;
374 :
375 E : return true;
376 E : }
377 :
378 E : bool ParseEngine::DispatchBatchInvocationEvent(EVENT_TRACE* event) {
379 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
380 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
381 E : DCHECK(!error_occurred_);
382 :
383 E : BinaryBufferReader reader(event->MofData, event->MofLength);
384 E : if (event->MofLength % sizeof(InvocationInfo) != 0) {
385 i : LOG(ERROR) << "Invocation batch length off.";
386 i : return false;
387 : }
388 :
389 E : const TraceBatchInvocationInfo* data = nullptr;
390 E : if (!reader.Read(event->MofLength, &data)) {
391 i : LOG(ERROR) << "Short or empty batch event.";
392 i : return false;
393 : }
394 :
395 : // TODO(rogerm): Ensure this is robust in the presence of incomplete write.
396 E : size_t num_invocations = event->MofLength / sizeof(InvocationInfo);
397 E : base::Time time(base::Time::FromFileTime(
398 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
399 E : DWORD process_id = event->Header.ProcessId;
400 E : DWORD thread_id = event->Header.ThreadId;
401 E : event_handler_->OnInvocationBatch(time,
402 : process_id,
403 : thread_id,
404 : num_invocations,
405 : data);
406 :
407 E : return true;
408 E : }
409 :
410 E : bool ParseEngine::DispatchThreadNameEvent(EVENT_TRACE* event) {
411 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
412 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
413 E : DCHECK(!error_occurred_);
414 :
415 E : BinaryBufferReader reader(event->MofData, event->MofLength);
416 E : const char* thread_name = nullptr;
417 E : size_t thread_name_len = 0;
418 E : if (!reader.ReadString(&thread_name, &thread_name_len)) {
419 i : LOG(ERROR) << "Unable to read string.";
420 i : return false;
421 : }
422 :
423 E : base::Time time(base::Time::FromFileTime(
424 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
425 E : DWORD process_id = event->Header.ProcessId;
426 E : DWORD thread_id = event->Header.ThreadId;
427 E : event_handler_->OnThreadName(time,
428 : process_id,
429 : thread_id,
430 : base::StringPiece(thread_name, thread_name_len));
431 :
432 E : return true;
433 E : }
434 :
435 E : bool ParseEngine::DispatchIndexedFrequencyEvent(EVENT_TRACE* event) {
436 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
437 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
438 E : DCHECK(!error_occurred_);
439 :
440 E : if (event->MofLength < sizeof(TraceIndexedFrequencyData)) {
441 E : LOG(ERROR) << "Data too small for TraceIndexedFrequency struct.";
442 E : return false;
443 : }
444 :
445 E : BinaryBufferReader reader(event->MofData, event->MofLength);
446 E : const TraceIndexedFrequencyData* data = nullptr;
447 E : if (!reader.Read(&data)) {
448 i : LOG(ERROR) << "Short or empty coverage data event.";
449 i : return false;
450 : }
451 E : DCHECK(data != nullptr);
452 :
453 : // Calculate the expected size of the entire payload, headers included.
454 : size_t expected_length = data->frequency_size * data->num_entries +
455 E : sizeof(TraceIndexedFrequencyData) - 1;
456 E : if (event->MofLength < expected_length) {
457 E : LOG(ERROR) << "Payload smaller than size implied by "
458 : << "TraceIndexedFrequencyData header.";
459 E : return false;
460 : }
461 :
462 E : base::Time time(base::Time::FromFileTime(
463 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
464 E : DWORD process_id = event->Header.ProcessId;
465 E : DWORD thread_id = event->Header.ThreadId;
466 E : event_handler_->OnIndexedFrequency(time, process_id, thread_id, data);
467 :
468 E : return true;
469 E : }
470 :
471 E : bool ParseEngine::DispatchDynamicSymbolEvent(EVENT_TRACE* event) {
472 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
473 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
474 E : DCHECK(!error_occurred_);
475 :
476 E : BinaryBufferReader reader(event->MofData, event->MofLength);
477 E : const TraceDynamicSymbol* symbol = nullptr;
478 E : const char* symbol_name = nullptr;
479 E : size_t symbol_name_len = 0;
480 E : if (!reader.Read(FIELD_OFFSET(TraceDynamicSymbol, symbol_name), &symbol) ||
481 : !reader.ReadString(&symbol_name, &symbol_name_len)) {
482 E : LOG(ERROR) << "Short or empty coverage data event.";
483 E : return false;
484 : }
485 :
486 E : DWORD process_id = event->Header.ProcessId;
487 E : event_handler_->OnDynamicSymbol(
488 : process_id, symbol->symbol_id,
489 : base::StringPiece(symbol_name, symbol_name_len));
490 :
491 E : return true;
492 E : }
493 :
494 E : bool ParseEngine::DispatchSampleDataEvent(EVENT_TRACE* event) {
495 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
496 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
497 E : DCHECK(!error_occurred_);
498 :
499 E : BinaryBufferReader reader(event->MofData, event->MofLength);
500 E : const TraceSampleData* data = nullptr;
501 E : if (!reader.Read(&data)) {
502 i : LOG(ERROR) << "Short or empty TraceSampleData event.";
503 i : return false;
504 : }
505 E : DCHECK(data != nullptr);
506 :
507 : // Calculate the expected size of the entire payload, headers included.
508 E : size_t expected_length = FIELD_OFFSET(TraceSampleData, buckets) +
509 : sizeof(data->buckets[0]) * data->bucket_count;
510 E : if (event->MofLength < expected_length) {
511 E : LOG(ERROR) << "Payload smaller than size implied by TraceSampleData "
512 : << "header.";
513 E : return false;
514 : }
515 :
516 E : base::Time time(base::Time::FromFileTime(
517 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
518 E : DWORD process_id = event->Header.ProcessId;
519 E : event_handler_->OnSampleData(time, process_id, data);
520 :
521 E : return true;
522 E : }
523 :
524 E : bool ParseEngine::DispatchFunctionNameTableEntryEvent(EVENT_TRACE* event) {
525 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
526 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
527 E : DCHECK(!error_occurred_);
528 :
529 E : BinaryBufferReader reader(event->MofData, event->MofLength);
530 E : const TraceFunctionNameTableEntry* data = nullptr;
531 E : if (!reader.Read(&data)) {
532 i : LOG(ERROR) << "Short or empty TraceFunctionNameTableEntry event.";
533 i : return false;
534 : }
535 E : DCHECK(data != nullptr);
536 :
537 : // Calculate the expected size of the payload and ensure there's
538 : // enough data.
539 E : size_t expected_length = FIELD_OFFSET(TraceFunctionNameTableEntry, name) +
540 : data->name_length;
541 E : if (event->MofLength < expected_length) {
542 E : LOG(ERROR) << "Payload smaller than size implied by "
543 : << "TraceFunctionNameTableEntry header.";
544 E : return false;
545 : }
546 :
547 E : base::Time time(base::Time::FromFileTime(
548 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
549 E : DWORD process_id = event->Header.ProcessId;
550 E : event_handler_->OnFunctionNameTableEntry(time, process_id, data);
551 :
552 E : return true;
553 E : }
554 :
555 E : bool ParseEngine::DispatchStackTrace(EVENT_TRACE* event) {
556 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
557 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
558 E : DCHECK(!error_occurred_);
559 :
560 E : BinaryBufferReader reader(event->MofData, event->MofLength);
561 E : const TraceStackTrace* data = nullptr;
562 E : if (!reader.Read(&data)) {
563 i : LOG(ERROR) << "Short or empty TraceStackTrace event.";
564 i : return false;
565 : }
566 E : DCHECK(data != nullptr);
567 :
568 : // Calculate the expected size of the payload and ensure there's
569 : // enough data.
570 E : size_t expected_length = FIELD_OFFSET(TraceStackTrace, frames) +
571 : data->num_frames * sizeof(void*);
572 E : if (event->MofLength < expected_length) {
573 E : LOG(ERROR) << "Payload smaller than size implied by "
574 : << "TraceStackTrace header.";
575 E : return false;
576 : }
577 :
578 E : base::Time time(base::Time::FromFileTime(
579 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
580 E : DWORD process_id = event->Header.ProcessId;
581 E : event_handler_->OnStackTrace(time, process_id, data);
582 :
583 E : return true;
584 E : }
585 :
586 E : bool ParseEngine::DispatchDetailedFunctionCall(EVENT_TRACE* event) {
587 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
588 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
589 E : DCHECK(!error_occurred_);
590 :
591 E : BinaryBufferReader reader(event->MofData, event->MofLength);
592 E : const TraceDetailedFunctionCall* data = nullptr;
593 E : if (!reader.Read(&data)) {
594 i : LOG(ERROR) << "Short or empty TraceDetailedFunctionCall event.";
595 i : return false;
596 : }
597 E : DCHECK(data != nullptr);
598 :
599 : // Calculate the expected size of the payload and ensure there's
600 : // enough data.
601 : size_t expected_length =
602 E : FIELD_OFFSET(TraceDetailedFunctionCall, argument_data) +
603 : data->argument_data_size;
604 E : if (event->MofLength < expected_length) {
605 E : LOG(ERROR) << "Payload smaller than size implied by "
606 : << "TraceDetailedFunctionCall header.";
607 E : return false;
608 : }
609 :
610 E : base::Time time(base::Time::FromFileTime(
611 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
612 E : DWORD process_id = event->Header.ProcessId;
613 E : DWORD thread_id = event->Header.ThreadId;
614 E : event_handler_->OnDetailedFunctionCall(time, process_id, thread_id, data);
615 :
616 E : return true;
617 E : }
618 :
619 E : bool ParseEngine::DispatchComment(EVENT_TRACE* event) {
620 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
621 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
622 E : DCHECK(!error_occurred_);
623 :
624 E : BinaryBufferReader reader(event->MofData, event->MofLength);
625 E : const TraceComment* data = nullptr;
626 E : if (!reader.Read(&data)) {
627 i : LOG(ERROR) << "Short or empty TraceComment event.";
628 i : return false;
629 : }
630 E : DCHECK(data != nullptr);
631 :
632 : // Calculate the expected size of the payload and ensure there's
633 : // enough data.
634 : size_t expected_length =
635 E : FIELD_OFFSET(TraceComment, comment) +
636 : data->comment_size;
637 E : if (event->MofLength < expected_length) {
638 E : LOG(ERROR) << "Payload smaller than size implied by "
639 : << "TraceComment header.";
640 E : return false;
641 : }
642 :
643 E : base::Time time(base::Time::FromFileTime(
644 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
645 E : DWORD process_id = event->Header.ProcessId;
646 E : event_handler_->OnComment(time, process_id, data);
647 :
648 E : return true;
649 E : }
650 :
651 E : bool ParseEngine::DispatchProcessHeap(EVENT_TRACE* event) {
652 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
653 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
654 E : DCHECK(!error_occurred_);
655 :
656 E : BinaryBufferReader reader(event->MofData, event->MofLength);
657 E : const TraceProcessHeap* data = nullptr;
658 E : if (!reader.Read(&data)) {
659 E : LOG(ERROR) << "Short or empty TraceProcessHeap event.";
660 E : return false;
661 : }
662 E : DCHECK(data != nullptr);
663 :
664 E : base::Time time(base::Time::FromFileTime(
665 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
666 E : DWORD process_id = event->Header.ProcessId;
667 E : event_handler_->OnProcessHeap(time, process_id, data);
668 :
669 E : return true;
670 E : }
671 :
672 : namespace {
673 :
674 : void ModuleTraceDataToModuleInformation(
675 : const TraceModuleData& module_data,
676 E : ModuleInformation* module_info) {
677 E : DCHECK_NE(static_cast<ModuleInformation*>(nullptr), module_info);
678 E : module_info->base_address.set_value(
679 : reinterpret_cast<uint32_t>(module_data.module_base_addr));
680 E : module_info->module_size = module_data.module_base_size;
681 E : module_info->path = module_data.module_name;
682 E : module_info->module_checksum = module_data.module_checksum;
683 E : module_info->module_time_date_stamp = module_data.module_time_date_stamp;
684 E : }
685 :
686 : } // namespace
687 :
688 : bool ParseEngine::DispatchModuleEvent(EVENT_TRACE* event,
689 E : TraceEventType type) {
690 E : DCHECK_NE(static_cast<EVENT_TRACE*>(nullptr), event);
691 E : DCHECK_NE(static_cast<ParseEventHandler*>(nullptr), event_handler_);
692 E : DCHECK(!error_occurred_);
693 E : DCHECK(type == TRACE_PROCESS_ATTACH_EVENT ||
694 : type == TRACE_PROCESS_DETACH_EVENT ||
695 : type == TRACE_THREAD_ATTACH_EVENT ||
696 : type == TRACE_THREAD_DETACH_EVENT);
697 :
698 E : BinaryBufferReader reader(event->MofData, event->MofLength);
699 E : const TraceModuleData* data = nullptr;
700 E : if (!reader.Read(&data)) {
701 E : LOG(ERROR) << "Short or empty module event.";
702 E : return false;
703 : }
704 :
705 E : if (data->module_base_addr == nullptr) {
706 E : LOG(INFO) << "Encountered incompletely written module event record.";
707 E : return true;
708 : }
709 :
710 E : base::Time time(base::Time::FromFileTime(
711 : reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
712 E : DWORD process_id = event->Header.ProcessId;
713 E : DWORD thread_id = event->Header.ThreadId;
714 :
715 E : switch (type) {
716 : case TRACE_PROCESS_ATTACH_EVENT: {
717 E : ModuleInformation module_info;
718 E : ModuleTraceDataToModuleInformation(*data, &module_info);
719 E : AddModuleInformation(process_id, module_info);
720 E : event_handler_->OnProcessAttach(time, process_id, thread_id, data);
721 E : break;
722 i : }
723 :
724 : case TRACE_PROCESS_DETACH_EVENT: {
725 E : event_handler_->OnProcessDetach(time, process_id, thread_id, data);
726 E : ModuleInformation module_info;
727 E : ModuleTraceDataToModuleInformation(*data, &module_info);
728 E : RemoveModuleInformation(process_id, module_info);
729 E : break;
730 i : }
731 :
732 : case TRACE_THREAD_ATTACH_EVENT: {
733 E : event_handler_->OnThreadAttach(time, process_id, thread_id, data);
734 E : break;
735 : }
736 :
737 : case TRACE_THREAD_DETACH_EVENT: {
738 E : event_handler_->OnThreadDetach(time, process_id, thread_id, data);
739 E : break;
740 : }
741 :
742 : default: {
743 i : LOG(ERROR) << "Unexpected module event type " << type << ".";
744 i : return false;
745 : }
746 : }
747 :
748 E : return true;
749 E : }
750 :
751 : } // namespace parser
752 : } // namespace trace
|