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