Coverage for /Syzygy/trace/parse/parse_engine.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
90.5%2482740.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc.
   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 "sawbuck/common/buffer_parser.h"
  24    :  #include "sawbuck/common/com_utils.h"
  25    :  #include "syzygy/trace/parse/parser.h"
  26    :  
  27    :  namespace trace {
  28    :  namespace parser {
  29    :  
  30    :  ParseEngine::ParseEngine(const char* name, bool fail_on_module_conflict)
  31    :      : event_handler_(NULL),
  32    :        error_occurred_(false),
  33  E :        fail_on_module_conflict_(fail_on_module_conflict) {
  34  E :    DCHECK(name != NULL);
  35  E :    DCHECK(name[0] != '\0');
  36  E :    name_ = name;
  37  E :  }
  38    :  
  39  E :  ParseEngine::~ParseEngine() {
  40  E :  }
  41    :  
  42  E :  const char* ParseEngine::name() const {
  43  E :    return name_.c_str();
  44  E :  }
  45    :  
  46  E :  bool ParseEngine::error_occurred() const {
  47  E :    return error_occurred_;
  48  E :  }
  49    :  
  50  E :  void ParseEngine::set_error_occurred(bool value) {
  51  E :    error_occurred_ = value;
  52  E :  }
  53    :  
  54  E :  void ParseEngine::set_event_handler(ParseEventHandler* event_handler) {
  55  E :    DCHECK(event_handler_ == NULL);
  56  E :    DCHECK(event_handler != NULL);
  57  E :    event_handler_ = event_handler;
  58  E :  }
  59    :  
  60    :  const ModuleInformation* ParseEngine::GetModuleInformation(
  61  E :      uint32 process_id, AbsoluteAddress64 addr) const {
  62  E :    ProcessMap::const_iterator processes_it = processes_.find(process_id);
  63  E :    if (processes_it == processes_.end())
  64  E :      return NULL;
  65    :  
  66  E :    const ModuleSpace& module_space = processes_it->second;
  67  E :    ModuleSpace::Range range(addr, 1);
  68    :    ModuleSpace::RangeMapConstIter module_it =
  69  E :        module_space.FindFirstIntersection(range);
  70  E :    if (module_it == module_space.end())
  71  E :      return NULL;
  72    :  
  73  E :    return &module_it->second;
  74  E :  }
  75    :  
  76    :  bool ParseEngine::AddModuleInformation(DWORD process_id,
  77  E :                                         const ModuleInformation& module_info) {
  78    :    // Avoid doing needless work.
  79  E :    if (module_info.module_size == 0)
  80  i :      return true;
  81    :  
  82    :    // This happens in Windows XP ETW traces for some reason. They contain
  83    :    // conflicing information, so we ignore them.
  84  E :    if (module_info.image_file_name.empty())
  85  i :      return true;
  86    :  
  87  E :    ModuleSpace& module_space = processes_[process_id];
  88  E :    AbsoluteAddress64 addr(module_info.base_address);
  89  E :    ModuleSpace::Range range(addr, module_info.module_size);
  90    :  
  91  E :    AnnotatedModuleInformation new_module_info(module_info);
  92    :  
  93  E :    ModuleSpace::RangeMapIter iter;
  94  E :    if (module_space.FindOrInsert(range, new_module_info, &iter)) {
  95  E :      return true;
  96    :    }
  97    :  
  98    :    // Perhaps this is a case of conflicting paths for the same module. We often
  99    :    // get paths reported to us in \Device\HarddiskVolumeN\... notation, and
 100    :    // othertimes in C:\... notation. In this case we're happy if everything
 101    :    // matches except the path. For a little bit of extra sanity checking we
 102    :    // also check the basename of the paths.
 103    :    if (module_info.base_address == iter->second.base_address &&
 104    :        module_info.image_checksum == iter->second.image_checksum &&
 105    :        module_info.module_size == iter->second.module_size &&
 106  E :        module_info.time_date_stamp == iter->second.time_date_stamp) {
 107  E :      FilePath path1(module_info.image_file_name);
 108  E :      FilePath path2(iter->second.image_file_name);
 109  E :      if (path1.BaseName() == path2.BaseName()) {
 110  E :        return true;
 111    :      }
 112  i :    }
 113    :  
 114    :    // Perhaps this is a case of process id reuse. In that case, we should have
 115    :    // previously seen a module unload event and marked the module information
 116    :    // as dirty.
 117  E :    while (iter->second.is_dirty) {
 118  E :      module_space.Remove(iter->first);
 119  E :      if (module_space.FindOrInsert(range, new_module_info, &iter)) {
 120  E :        return true;
 121    :      }
 122  i :    }
 123    :  
 124  E :    LOG(ERROR) << "Conflicting module info for pid=" << process_id << ": "
 125    :               << module_info.image_file_name
 126    :               << " (base=0x" << module_info.base_address
 127    :               << ", size=" << module_info.module_size << ") and "
 128    :               << iter->second.image_file_name
 129    :               << " (base=0x" << iter->second.base_address
 130    :               << ", size=" << iter->second.module_size << ").";
 131    :  
 132  E :    return fail_on_module_conflict_ ? false : true;
 133  E :  }
 134    :  
 135    :  bool ParseEngine::RemoveModuleInformation(
 136  E :      DWORD process_id, const ModuleInformation& module_info) {
 137    :    // Avoid doing needless work.
 138  E :    if (module_info.module_size == 0)
 139  i :      return true;
 140    :  
 141    :    // This happens in Windows XP traces for some reason. They contain conflicing
 142    :    // information, so we ignore them.
 143  E :    if (module_info.image_file_name.empty())
 144  i :      return true;
 145    :  
 146  E :    ModuleSpace& module_space = processes_[process_id];
 147  E :    AbsoluteAddress64 addr(module_info.base_address);
 148  E :    ModuleSpace::Range range(addr, module_info.module_size);
 149  E :    ModuleSpace::RangeMapIter it = module_space.FindFirstIntersection(range);
 150  E :    if (it == module_space.end()) {
 151    :      // We occasionally see this, as certain modules fire off multiple Unload
 152    :      // events, so we don't log an error. I'm looking at you, logman.exe.
 153  E :      return true;
 154    :    }
 155  E :    if (it->first != range) {
 156  i :      LOG(ERROR) << "Trying to remove module with mismatching range: "
 157    :                 << module_info.image_file_name
 158    :                 << " (base=0x" << module_info.base_address
 159    :                 << ", size=" << module_info.module_size << ").";
 160  i :      if (fail_on_module_conflict_)
 161  i :        return false;
 162    :    }
 163    :  
 164    :    // We only remove modules from a given process if a conflicting module is
 165    :    // loaded after the module has been marked as dirty. This is because (1) we
 166    :    // don't guarantee temporal order of all events in a process, so you
 167    :    // might parse a function event after seeing the module get unloaded
 168    :    // if the buffers are flushed in that order; and (2) because process ids may
 169    :    // be reused (but not concurrently) so we do want to drop stale module info
 170    :    // when the process has been replaced.
 171    :  
 172  E :    it->second.is_dirty = true;
 173    :  
 174  E :    return true;
 175  E :  }
 176    :  
 177  E :  bool ParseEngine::RemoveProcessInformation(DWORD process_id) {
 178  E :    ProcessMap::iterator proc_iter = processes_.find(process_id);
 179  E :    if (proc_iter == processes_.end()) {
 180  i :      LOG(ERROR) << "Unknown process id: " << process_id << ".";
 181  i :      return false;
 182    :    }
 183    :  
 184  E :    ModuleSpace& process_info = proc_iter->second;
 185    :  
 186  E :    ModuleSpace::iterator module_iter = process_info.begin();
 187  E :    for (; module_iter != process_info.end(); ++module_iter) {
 188  E :      module_iter->second.is_dirty = true;
 189  E :    }
 190    :  
 191  E :    return true;
 192  E :  }
 193    :  
 194  E :  bool ParseEngine::DispatchEvent(EVENT_TRACE* event) {
 195  E :    DCHECK(event != NULL);
 196  E :    DCHECK(event_handler_ != NULL);
 197  E :    DCHECK(error_occurred_ == false);
 198    :  
 199  E :    if (kCallTraceEventClass != event->Header.Guid)
 200  E :      return false;
 201    :  
 202  E :    bool success = false;
 203  E :    TraceEventType type = static_cast<TraceEventType>(event->Header.Class.Type);
 204    :  
 205  E :    switch (type) {
 206    :      case TRACE_ENTER_EVENT:
 207    :      case TRACE_EXIT_EVENT:
 208  E :        success = DispatchEntryExitEvent(event, type);
 209  E :        break;
 210    :  
 211    :      case TRACE_BATCH_ENTER:
 212  E :        success = DispatchBatchEnterEvent(event);
 213  E :        break;
 214    :  
 215    :      case TRACE_PROCESS_ATTACH_EVENT:
 216    :      case TRACE_PROCESS_DETACH_EVENT:
 217    :      case TRACE_THREAD_ATTACH_EVENT:
 218    :      case TRACE_THREAD_DETACH_EVENT:
 219  E :        success = DispatchModuleEvent(event, type);
 220  E :        break;
 221    :  
 222    :      case TRACE_PROCESS_ENDED:
 223  E :        success = DispatchProcessEndedEvent(event);
 224  E :        break;
 225    :  
 226    :      case TRACE_MODULE_EVENT:
 227  i :        LOG(ERROR) << "Parsing for TRACE_MODULE_EVENT not yet implemented.";
 228  i :        break;
 229    :  
 230    :      case TRACE_BATCH_INVOCATION:
 231  E :        success = DispatchBatchInvocationEvent(event);
 232  E :        break;
 233    :  
 234    :      case TRACE_THREAD_NAME:
 235  E :        success = DispatchThreadNameEvent(event);
 236  E :        break;
 237    :  
 238    :      case TRACE_BASIC_BLOCK_FREQUENCY:
 239  E :        success = DispatchBasicBlockFrequencyEvent(event);
 240  E :        break;
 241    :  
 242    :      default:
 243  E :        LOG(ERROR) << "Unknown event type encountered.";
 244    :        break;
 245    :    }
 246    :  
 247  E :    if (!success) {
 248  E :      error_occurred_ = true;
 249    :    }
 250    :  
 251  E :    return true;
 252  E :  }
 253    :  
 254    :  bool ParseEngine::DispatchEntryExitEvent(EVENT_TRACE* event,
 255  E :                                           TraceEventType type) {
 256  E :    DCHECK(event != NULL);
 257  E :    DCHECK(type == TRACE_ENTER_EVENT || type == TRACE_EXIT_EVENT);
 258  E :    DCHECK(event_handler_ != NULL);
 259  E :    DCHECK(error_occurred_ == false);
 260    :  
 261  E :    BinaryBufferReader reader(event->MofData, event->MofLength);
 262  E :    const TraceEnterExitEventData* data = NULL;
 263    :  
 264  E :    if (!reader.Read(FIELD_OFFSET(TraceEnterExitEventData, traces), &data)) {
 265  E :      LOG(ERROR) << "Short event header.";
 266  E :      return false;
 267    :    }
 268    :  
 269  E :    if (!reader.Consume(data->num_traces * sizeof(data->traces[0]))) {
 270  E :      LOG(ERROR) << "Short event tail.";
 271  E :      return false;
 272    :    }
 273    :  
 274    :    base::Time time(base::Time::FromFileTime(
 275  E :        reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
 276  E :    DWORD process_id = event->Header.ProcessId;
 277  E :    DWORD thread_id = event->Header.ThreadId;
 278    :  
 279  E :    switch (type) {
 280    :      case TRACE_ENTER_EVENT:
 281  E :        event_handler_->OnFunctionEntry(time, process_id, thread_id, data);
 282  E :        break;
 283    :  
 284    :      case TRACE_EXIT_EVENT:
 285  E :        event_handler_->OnFunctionExit(time, process_id, thread_id, data);
 286  E :        break;
 287    :  
 288    :      default:
 289  i :        NOTREACHED() << "Impossible event type.";
 290  i :        return false;
 291    :    }
 292    :  
 293  E :    return true;
 294  E :  }
 295    :  
 296  E :  bool ParseEngine::DispatchBatchEnterEvent(EVENT_TRACE* event) {
 297  E :    DCHECK(event != NULL);
 298  E :    DCHECK(event_handler_ != NULL);
 299  E :    DCHECK(error_occurred_ == false);
 300    :  
 301  E :    BinaryBufferReader reader(event->MofData, event->MofLength);
 302  E :    const TraceBatchEnterData* data = NULL;
 303  E :    size_t offset_to_calls = FIELD_OFFSET(TraceBatchEnterData, calls);
 304  E :    if (!reader.Read(offset_to_calls, &data)) {
 305  E :      LOG(ERROR) << "Short or empty batch event.";
 306  E :      return false;
 307    :    }
 308    :  
 309  E :    size_t bytes_needed = data->num_calls * sizeof(data->calls[0]);
 310  E :    if (!reader.Consume(bytes_needed)) {
 311  E :      LOG(ERROR) << "Short batch event data. Expected " << data->num_calls
 312    :                 << " entries (" << (offset_to_calls + bytes_needed)
 313    :                 << " bytes) but batch record was only " << event->MofLength
 314    :                 << " bytes.";
 315  E :      return false;
 316    :    }
 317    :  
 318    :    // Trim the batch entries if the last one is NULL, indicating that the
 319    :    // reporting thread was interrupted mid-write.
 320    :    if (data->num_calls != 0 &&
 321  E :        data->calls[data->num_calls - 1].function == NULL) {
 322    :      // Yuck! Cast away constness because the BinaryBufferReader only likes
 323    :      // to deal with const output pointers.
 324  E :      const_cast<TraceBatchEnterData*>(data)->num_calls -= 1;
 325    :    }
 326    :    DCHECK(data->num_calls == 0 ||
 327  E :           data->calls[data->num_calls - 1].function != NULL);
 328    :  
 329    :    base::Time time(base::Time::FromFileTime(
 330  E :        reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
 331  E :    DWORD process_id = event->Header.ProcessId;
 332  E :    DWORD thread_id = data->thread_id;
 333  E :    event_handler_->OnBatchFunctionEntry(time, process_id, thread_id, data);
 334    :  
 335  E :    return true;
 336  E :  }
 337    :  
 338  E :  bool ParseEngine::DispatchProcessEndedEvent(EVENT_TRACE* event) {
 339  E :    DCHECK(event != NULL);
 340  E :    DCHECK(event_handler_ != NULL);
 341  E :    DCHECK(error_occurred_ == false);
 342    :  
 343    :    base::Time time(base::Time::FromFileTime(
 344  E :        reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
 345    :  
 346  E :    event_handler_->OnProcessEnded(time, event->Header.ProcessId);
 347  E :    if (!RemoveProcessInformation(event->Header.ProcessId))
 348  i :      return false;
 349    :  
 350  E :    return true;
 351  E :  }
 352    :  
 353  E :  bool ParseEngine::DispatchBatchInvocationEvent(EVENT_TRACE* event) {
 354  E :    DCHECK(event != NULL);
 355  E :    DCHECK(event_handler_ != NULL);
 356  E :    DCHECK(error_occurred_ == false);
 357    :  
 358  E :    BinaryBufferReader reader(event->MofData, event->MofLength);
 359  E :    if (event->MofLength % sizeof(InvocationInfo) != 0) {
 360  i :      LOG(ERROR) << "Invocation batch length off.";
 361  i :      return false;
 362    :    }
 363    :  
 364  E :    const TraceBatchInvocationInfo* data = NULL;
 365  E :    if (!reader.Read(event->MofLength, &data)) {
 366  i :      LOG(ERROR) << "Short or empty batch event.";
 367  i :      return false;
 368    :    }
 369    :  
 370    :    // TODO(rogerm): Ensure this is robust in the presence of incomplete write.
 371  E :    size_t num_invocations = event->MofLength / sizeof(InvocationInfo);
 372    :    base::Time time(base::Time::FromFileTime(
 373  E :        reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
 374  E :    DWORD process_id = event->Header.ProcessId;
 375  E :    DWORD thread_id = event->Header.ThreadId;
 376    :    event_handler_->OnInvocationBatch(time,
 377    :                                      process_id,
 378    :                                      thread_id,
 379    :                                      num_invocations,
 380  E :                                      data);
 381    :  
 382  E :    return true;
 383  E :  }
 384    :  
 385  E :  bool ParseEngine::DispatchThreadNameEvent(EVENT_TRACE* event) {
 386  E :    DCHECK(event != NULL);
 387  E :    DCHECK(event_handler_ != NULL);
 388  E :    DCHECK(error_occurred_ == false);
 389    :  
 390  E :    BinaryBufferReader reader(event->MofData, event->MofLength);
 391  E :    const char* thread_name = NULL;
 392  E :    size_t thread_name_len = 0;
 393  E :    if (!reader.ReadString(&thread_name, &thread_name_len)) {
 394  i :      LOG(ERROR) << "Unable to read string.";
 395  i :      return false;
 396    :    }
 397    :  
 398    :    base::Time time(base::Time::FromFileTime(
 399  E :        reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
 400  E :    DWORD process_id = event->Header.ProcessId;
 401  E :    DWORD thread_id = event->Header.ThreadId;
 402    :    event_handler_->OnThreadName(time,
 403    :                                 process_id,
 404    :                                 thread_id,
 405  E :                                 base::StringPiece(thread_name, thread_name_len));
 406    :  
 407  E :    return true;
 408  E :  }
 409    :  
 410  E :  bool ParseEngine::DispatchBasicBlockFrequencyEvent(EVENT_TRACE* event) {
 411  E :    DCHECK(event != NULL);
 412  E :    DCHECK(event_handler_ != NULL);
 413  E :    DCHECK(error_occurred_ == false);
 414    :  
 415  E :    if (event->MofLength < sizeof(TraceBasicBlockFrequencyData)) {
 416  E :      LOG(ERROR) << "Data too small for TraceBasicBlockFrequency struct.";
 417  E :      return false;
 418    :    }
 419    :  
 420  E :    BinaryBufferReader reader(event->MofData, event->MofLength);
 421  E :    const TraceBasicBlockFrequencyData* data = NULL;
 422  E :    if (!reader.Read(&data)) {
 423  i :      LOG(ERROR) << "Short or empty coverage data event.";
 424  i :      return false;
 425    :    }
 426  E :    DCHECK(data != NULL);
 427    :  
 428    :    // Calculate the expected size of the entire payload, headers included.
 429    :    size_t expected_length = data->frequency_size * data->num_basic_blocks +
 430  E :        sizeof(TraceBasicBlockFrequencyData) - 1;
 431  E :    if (event->MofLength < expected_length) {
 432  E :      LOG(ERROR) << "Payload smaller than size implied by "
 433    :                 << "TraceBasicBlockFrequencyData header.";
 434  E :      return false;
 435    :    }
 436    :  
 437    :    base::Time time(base::Time::FromFileTime(
 438  E :        reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
 439  E :    DWORD process_id = event->Header.ProcessId;
 440  E :    DWORD thread_id = event->Header.ThreadId;
 441  E :    event_handler_->OnBasicBlockFrequency(time, process_id, thread_id, data);
 442    :  
 443  E :    return true;
 444  E :  }
 445    :  
 446    :  namespace {
 447    :  
 448    :  ModuleInformation ModuleTraceDataToModuleInformation(
 449  E :      const TraceModuleData& module_data) {
 450  E :    ModuleInformation module_info = {};
 451    :    module_info.base_address =
 452  E :        reinterpret_cast<uint32>(module_data.module_base_addr);
 453  E :    module_info.module_size = module_data.module_base_size;
 454  E :    module_info.image_file_name = module_data.module_name;
 455  E :    module_info.image_checksum = module_data.module_checksum;
 456  E :    module_info.time_date_stamp = module_data.module_time_date_stamp;
 457  E :    return module_info;
 458  E :  }
 459    :  
 460    :  }  // namespace
 461    :  
 462    :  bool ParseEngine::DispatchModuleEvent(EVENT_TRACE* event,
 463  E :                                        TraceEventType type) {
 464  E :    DCHECK(event != NULL);
 465    :    DCHECK(type == TRACE_PROCESS_ATTACH_EVENT ||
 466    :           type == TRACE_PROCESS_DETACH_EVENT ||
 467    :           type == TRACE_THREAD_ATTACH_EVENT ||
 468  E :           type == TRACE_THREAD_DETACH_EVENT);
 469  E :    DCHECK(event_handler_ != NULL);
 470  E :    DCHECK(error_occurred_ == false);
 471    :  
 472  E :    BinaryBufferReader reader(event->MofData, event->MofLength);
 473  E :    const TraceModuleData* data = NULL;
 474  E :    if (!reader.Read(&data)) {
 475  E :      LOG(ERROR) << "Short or empty module event.";
 476  E :      return false;
 477    :    }
 478    :  
 479  E :    if (data->module_base_addr == NULL) {
 480  E :      LOG(INFO) << "Encountered incompletely written module event record.";
 481  E :      return true;
 482    :    }
 483    :  
 484    :    base::Time time(base::Time::FromFileTime(
 485  E :        reinterpret_cast<FILETIME&>(event->Header.TimeStamp)));
 486  E :    DWORD process_id = event->Header.ProcessId;
 487  E :    DWORD thread_id = event->Header.ThreadId;
 488    :  
 489  E :    switch (type) {
 490    :      case TRACE_PROCESS_ATTACH_EVENT:
 491    :        AddModuleInformation(process_id,
 492  E :                             ModuleTraceDataToModuleInformation(*data));
 493  E :        event_handler_->OnProcessAttach(time, process_id, thread_id, data);
 494  E :        break;
 495    :  
 496    :      case TRACE_PROCESS_DETACH_EVENT:
 497  E :        event_handler_->OnProcessDetach(time, process_id, thread_id, data);
 498    :        RemoveModuleInformation(process_id,
 499  E :                                ModuleTraceDataToModuleInformation(*data));
 500  E :        break;
 501    :  
 502    :      case TRACE_THREAD_ATTACH_EVENT:
 503  E :        event_handler_->OnThreadAttach(time, process_id, thread_id, data);
 504  E :        break;
 505    :  
 506    :      case TRACE_THREAD_DETACH_EVENT:
 507  E :        event_handler_->OnThreadDetach(time, process_id, thread_id, data);
 508  E :        break;
 509    :  
 510    :      default:
 511  i :        LOG(ERROR) << "Unexpected module event type " << type << ".";
 512  i :        return false;
 513    :    }
 514    :  
 515  E :    return true;
 516  E :  }
 517    :  
 518    :  }  // namespace trace::parser
 519    :  }  // namespace trace

Coverage information generated Thu Sep 06 11:30:46 2012.