Coverage for /Syzygy/trace/client/client_utils.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
84.0%1571870.C++source

Line-by-line coverage:

   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    :  // Declares utility functions used by the call trace client and its unit
  16    :  // tests.
  17    :  
  18    :  #include "syzygy/trace/client/client_utils.h"
  19    :  
  20    :  #include <psapi.h>
  21    :  
  22    :  #include "base/environment.h"
  23    :  #include "base/file_util.h"
  24    :  #include "base/logging.h"
  25    :  #include "base/string_number_conversions.h"
  26    :  #include "base/string_split.h"
  27    :  #include "base/utf_string_conversions.h"
  28    :  #include "base/win/pe_image.h"
  29    :  #include "sawbuck/common/com_utils.h"
  30    :  #include "syzygy/common/path_util.h"
  31    :  #include "syzygy/core/file_util.h"
  32    :  #include "syzygy/trace/client/rpc_session.h"
  33    :  
  34    :  // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
  35    :  extern "C" IMAGE_DOS_HEADER __ImageBase;
  36    :  
  37    :  namespace trace {
  38    :  namespace client {
  39    :  
  40    :  namespace {
  41    :  
  42    :  // Loads the environment variable @p env_var and splits it at semi-colons. Each
  43    :  // substring is treated as a comma-separated "path,value" pair, with the first
  44    :  // substring being allowed to be a "value" singleton interpreted as a default
  45    :  // value. Looks for the presence of @p module_path in the pairs, with more
  46    :  // exact matches taking higher priority (highest is exact path matching,
  47    :  // than basename matching and finally the default value).
  48    :  //
  49    :  // Returns true if a value has been found via the environment variable, false
  50    :  // if no environment variable exists or no match was found. If no match is
  51    :  // found @p value is left unmodified.
  52    :  template<typename ReturnType, typename ConversionFunctor>
  53    :  bool GetModuleValueFromEnvVar(const char* env_var_name,
  54    :                                const FilePath& module_path,
  55    :                                const ReturnType& default_value,
  56    :                                const ConversionFunctor& convert,
  57  E :                                ReturnType* value) {
  58  E :    size_t best_score = 0;
  59  E :    ReturnType best_value = default_value;
  60    :  
  61    :    // Get the environment variable. If it's empty, we can return early.
  62  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
  63  E :    std::string env_var;
  64  E :    env->GetVar(env_var_name, &env_var);
  65  E :    if (env_var.empty())
  66  E :      return false;
  67    :  
  68    :    // Get the absolute path and the basename of the module. We will use these
  69    :    // for matching.
  70  E :    FilePath abs_module_path(module_path);
  71  E :    CHECK(file_util::AbsolutePath(&abs_module_path));
  72  E :    FilePath base_module_path = module_path.BaseName();
  73    :  
  74  E :    std::vector<std::string> pairs;
  75  E :    base::SplitString(env_var, ';', &pairs);
  76    :  
  77  E :    for (size_t i = 0; i < pairs.size(); ++i) {
  78  E :      if (pairs[i].empty())
  79  i :        continue;
  80    :  
  81  E :      std::vector<std::string> path_value;
  82  E :      base::SplitString(pairs[i], ',', &path_value);
  83    :  
  84  E :      size_t score = 0;
  85    :  
  86    :      // Ignore malformed fields.
  87  E :      if (path_value.size() > 2)
  88  i :        continue;
  89    :  
  90    :      // Ignore entries with improperly formatted values.
  91  E :      ReturnType value = default_value;
  92  E :      if (!convert(path_value.back(), &value))
  93  E :        continue;
  94    :  
  95  E :      if (path_value.size() == 1) {
  96    :        // This is a default value specified without a path.
  97  E :        score = 1;
  98  E :      } else if (path_value.size() == 2) {
  99  E :        FilePath path(UTF8ToWide(path_value[0]));
 100    :  
 101    :        // Ignore improperly formatted paths.
 102  E :        if (path.empty())
 103  i :          continue;
 104    :  
 105  E :        if (base_module_path == path) {
 106    :          // The basename of the module matches the path.
 107  E :          score = 2;
 108  E :        } else if (abs_module_path == path) {
 109    :          // The full path of the module matches.
 110  E :          score = 3;
 111  E :        } else {
 112    :          // Due to mounting files in different locations we can often get
 113    :          // differing but equivalent paths to the same file. Thus, we pull out
 114    :          // the big guns and do a file-system level comparison to see if they
 115    :          // do in fact refer to the same file.
 116    :          core::FilePathCompareResult result = core::CompareFilePaths(
 117  E :              abs_module_path, path);
 118  E :          if (result == core::kEquivalentFilePaths)
 119  i :            score = 3;
 120    :        }
 121  E :      }
 122    :  
 123  E :      if (score > best_score) {
 124  E :        best_score = score;
 125  E :        best_value = value;
 126    :      }
 127  E :    }
 128    :  
 129  E :    if (best_score > 0) {
 130  E :      *value = best_value;
 131  E :      return true;
 132    :    }
 133    :  
 134  E :    return false;
 135  E :  }
 136    :  
 137    :  struct KeepAsString {
 138  E :    bool operator()(const std::string& s1, std::string* s2) const {
 139  E :      DCHECK(s2 != NULL);
 140  E :      *s2 = s1;
 141  E :      return true;
 142  E :    }
 143    :  };
 144    :  
 145    :  struct ToInt {
 146  E :    bool operator()(const std::string& s, int* i) const {
 147  E :      DCHECK(i != NULL);
 148  E :      if (!base::StringToInt(s, i))
 149  E :        return false;
 150  E :      return true;
 151  E :    }
 152    :  };
 153    :  
 154    :  }  // namespace
 155    :  
 156    :  int ReasonToEventType(DWORD reason) {
 157    :    switch (reason) {
 158    :      case DLL_PROCESS_ATTACH:
 159    :        return TRACE_PROCESS_ATTACH_EVENT;
 160    :  
 161    :      case DLL_PROCESS_DETACH:
 162    :        return TRACE_PROCESS_DETACH_EVENT;
 163    :  
 164    :      case DLL_THREAD_ATTACH:
 165    :        return TRACE_THREAD_ATTACH_EVENT;
 166    :  
 167    :      case DLL_THREAD_DETACH:
 168    :        return TRACE_THREAD_DETACH_EVENT;
 169    :  
 170    :      default:
 171    :        NOTREACHED() << "Invalid reason: " << reason << ".";
 172    :        return -1;
 173    :    }
 174    :  }
 175    :  
 176  E :  RecordPrefix* GetRecordPrefix(void *record) {
 177  E :    DCHECK(record != NULL);
 178    :  
 179  E :    return reinterpret_cast<RecordPrefix*>(record) - 1;
 180  E :  }
 181    :  
 182    :  TraceFileSegment::TraceFileSegment()
 183    :      : header(NULL),
 184    :        base_ptr(NULL),
 185    :        write_ptr(NULL),
 186  E :        end_ptr(NULL) {
 187    :    // Zero the RPC buffer.
 188  E :    memset(&buffer_info, 0, sizeof(buffer_info));
 189  E :  }
 190    :  
 191    :  // Returns true if there's enough space left in the given segment to write
 192    :  // num_bytes of raw data.
 193  E :  bool TraceFileSegment::CanAllocateRaw(size_t num_bytes) const {
 194  E :    DCHECK(write_ptr != NULL);
 195  E :    DCHECK(end_ptr != NULL);
 196  E :    DCHECK(num_bytes != 0);
 197  E :    return (write_ptr + num_bytes) <= end_ptr;
 198  E :  }
 199    :  
 200    :  // Returns true if there's enough space left in the given segment to write
 201    :  // a prefixed record of length num_bytes.
 202  E :  bool TraceFileSegment::CanAllocate(size_t num_bytes) const {
 203  E :    DCHECK(num_bytes != 0);
 204  E :    return CanAllocateRaw(num_bytes + sizeof(RecordPrefix));
 205  E :  }
 206    :  
 207  E :  void FillPrefix(RecordPrefix* prefix, int type, size_t size) {
 208  E :    prefix->size = size;
 209  E :    prefix->version.hi = TRACE_VERSION_HI;
 210  E :    prefix->version.lo = TRACE_VERSION_LO;
 211  E :    prefix->type = static_cast<uint16>(type);
 212  E :    prefix->timestamp = ::GetTickCount();
 213  E :  }
 214    :  
 215    :  // Writes the segment header at the top of a segment, updating the bytes
 216    :  // consumed and initializing the segment header structures.
 217  E :  void TraceFileSegment::WriteSegmentHeader(SessionHandle session_handle) {
 218  E :    DCHECK(header == NULL);
 219  E :    DCHECK(write_ptr != NULL);
 220  E :    DCHECK(CanAllocate(sizeof(TraceFileSegmentHeader)));
 221    :  
 222    :    // The trace record allocation will write the record prefix and update
 223    :    // the number of bytes consumed within the buffer.
 224    :  
 225  E :    RecordPrefix* prefix = reinterpret_cast<RecordPrefix*>(write_ptr);
 226    :    FillPrefix(prefix,
 227    :               TraceFileSegmentHeader::kTypeId,
 228  E :               sizeof(TraceFileSegmentHeader));
 229    :  
 230  E :    header = reinterpret_cast<TraceFileSegmentHeader*>(prefix + 1);
 231  E :    header->thread_id = ::GetCurrentThreadId();
 232  E :    header->segment_length = 0;
 233    :  
 234  E :    write_ptr = reinterpret_cast<uint8*>(header + 1);
 235  E :  }
 236    :  
 237    :  void* TraceFileSegment::AllocateTraceRecordImpl(int record_type,
 238  E :                                                  size_t record_size) {
 239  E :    DCHECK(header != NULL);
 240  E :    DCHECK(write_ptr != NULL);
 241  E :    DCHECK(record_size != 0);
 242    :  
 243  E :    const size_t total_size = sizeof(RecordPrefix) + record_size;
 244    :  
 245  E :    DCHECK(CanAllocateRaw(total_size));
 246    :  
 247    :    // Clear the memory we're about to allocate. If this thread gets killed
 248    :    // before it can finish updating the trace record we want the allocated
 249    :    // record to have a somewhat consistent state.
 250  E :    ::memset(write_ptr, 0, total_size);
 251    :  
 252  E :    RecordPrefix* prefix = reinterpret_cast<RecordPrefix*>(write_ptr);
 253  E :    FillPrefix(prefix, record_type, record_size);
 254    :  
 255  E :    write_ptr += total_size;
 256  E :    header->segment_length += total_size;
 257    :  
 258  E :    return prefix + 1;
 259  E :  }
 260    :  
 261  E :  bool GetModuleBaseAddress(void* address_in_module, void** module_base) {
 262  E :    DCHECK(address_in_module != NULL);
 263  E :    DCHECK(module_base != NULL);
 264    :  
 265    :    // Get the address of the module. We do this by querying for the allocation
 266    :    // that contains the address of the function we intercepted. This must lie
 267    :    // within the instrumented module, and be part of the single allocation in
 268    :    // which the image of the module lies. The base of the module will be the
 269    :    // base address of the allocation.
 270  E :    MEMORY_BASIC_INFORMATION mem_info = {};
 271  E :    if (::VirtualQuery(address_in_module, &mem_info, sizeof(mem_info)) == 0) {
 272  i :      DWORD error = ::GetLastError();
 273  i :      LOG(ERROR) << "VirtualQuery failed: " << com::LogWe(error) << ".";
 274  i :      return false;
 275    :    }
 276    :  
 277  E :    *module_base = mem_info.AllocationBase;
 278    :  
 279    :  #ifndef NDEBUG
 280  E :    base::win::PEImage image(*module_base);
 281  E :    DCHECK(image.VerifyMagic());
 282    :  #endif
 283    :  
 284  E :    return true;
 285  E :  }
 286    :  
 287  E :  bool GetModulePath(void* module_base, FilePath* module_path) {
 288  E :    DCHECK(module_base != NULL);
 289  E :    DCHECK(module_path != NULL);
 290    :  
 291  E :    HMODULE module = reinterpret_cast<HMODULE>(module_base);
 292    :  
 293    :    wchar_t buffer[1024];
 294    :    if (::GetMappedFileName(::GetCurrentProcess(), module_base, buffer,
 295  E :                            arraysize(buffer)) == 0) {
 296  i :      DWORD error = ::GetLastError();
 297  i :      LOG(ERROR) << "GetMappedFileName failed: " << com::LogWe(error) << ".";
 298  i :      return false;
 299    :    }
 300    :  
 301  E :    FilePath device_path(buffer);
 302  E :    if (!common::ConvertDevicePathToDrivePath(device_path, module_path))
 303  i :      return false;
 304    :  
 305  E :    return true;
 306  E :  }
 307    :  
 308  E :  std::string GetInstanceIdForModule(const FilePath& module_path) {
 309  E :    std::string id;
 310    :    // We don't care if the search is successful or not.
 311    :    GetModuleValueFromEnvVar(::kSyzygyRpcInstanceIdEnvVar, module_path,
 312  E :                             id, KeepAsString(), &id);
 313  E :    return id;
 314  E :  }
 315    :  
 316  E :  std::string GetInstanceIdForThisModule() {
 317  E :    FilePath module_path;
 318  E :    CHECK(GetModulePath(&__ImageBase, &module_path));
 319    :  
 320  E :    std::string instance_id = GetInstanceIdForModule(module_path);
 321    :  
 322  E :    return instance_id;
 323  E :  }
 324    :  
 325  E :  bool IsRpcSessionMandatory(const FilePath& module_path) {
 326  E :    int value = 0;
 327    :    if (!GetModuleValueFromEnvVar(kSyzygyRpcSessionMandatoryEnvVar, module_path,
 328  E :                                  value, ToInt(), &value)) {
 329  E :      return false;
 330    :    }
 331    :  
 332  E :    if (value == 0)
 333  E :      return false;
 334    :  
 335    :    // Anything non-zero is treated as 'true'.
 336  E :    return true;
 337  E :  }
 338    :  
 339  E :  bool IsRpcSessionMandatoryForThisModule() {
 340  E :    FilePath module_path;
 341  E :    CHECK(GetModulePath(&__ImageBase, &module_path));
 342    :  
 343  E :    if (IsRpcSessionMandatory(module_path))
 344  E :      return true;
 345    :  
 346  E :    return false;
 347  E :  }
 348    :  
 349  E :  bool InitializeRpcSession(RpcSession* rpc_session, TraceFileSegment* segment) {
 350  E :    DCHECK(rpc_session != NULL);
 351    :  
 352  E :    std::string id = trace::client::GetInstanceIdForThisModule();
 353  E :    rpc_session->set_instance_id(UTF8ToWide(id));
 354  E :    if (rpc_session->CreateSession(segment))
 355  E :      return true;
 356    :  
 357    :    // If the session is not mandatory then return and indicate that we failed
 358    :    // to initialize properly.
 359  E :    if (!IsRpcSessionMandatoryForThisModule())
 360  E :      return false;
 361    :  
 362    :    // If you're seeing this error message it's because the process was unable
 363    :    // to initialize an RPC sesion, and the state of the
 364    :    // SYZYGY_RPC_SESSION_MANDATORY environment variable indicated that it was
 365    :    // required. Make sure the call-trace service is running with the appropriate
 366    :    // instance ID!
 367  i :    LOG(ERROR) << "RPC session is mandatory, but unable to be created.";
 368    :  
 369    :    // Dump some context regarding the decision to abort.
 370  i :    FilePath module_path;
 371  i :    if (GetModulePath(&__ImageBase, &module_path))
 372  i :      LOG(ERROR) << "Module path: " << module_path.value();
 373    :  
 374  i :    LOG(ERROR) << "RPC instance ID is \"" << id << "\".";
 375    :  
 376  i :    base::Environment* env = base::Environment::Create();
 377  i :    if (env) {
 378  i :      std::string var;
 379  i :      if (env->GetVar(::kSyzygyRpcInstanceIdEnvVar, &var)) {
 380  i :        LOG(ERROR) << ::kSyzygyRpcInstanceIdEnvVar << " is \"" << var << "\".";
 381  i :      } else {
 382  i :        LOG(ERROR) << ::kSyzygyRpcInstanceIdEnvVar << " is not set.";
 383    :      }
 384    :  
 385  i :      if (env->GetVar(::kSyzygyRpcSessionMandatoryEnvVar, &var)) {
 386  i :        LOG(ERROR) << ::kSyzygyRpcSessionMandatoryEnvVar << " is \"" << var
 387    :                   << "\".";
 388  i :      } else {
 389  i :        LOG(ERROR) << ::kSyzygyRpcSessionMandatoryEnvVar << " is not set.";
 390    :      }
 391  i :    }
 392    :  
 393    :    // Kill this process with prejudice. We need to be heavy handed here because
 394    :    // we are typically running under the loader lock, and most things won't
 395    :    // actually convince it to stop the entire process.
 396  i :    ::TerminateProcess(::GetCurrentProcess(), 255);
 397    :  
 398    :    // We need this to avoid getting complaints about control paths missing a
 399    :    // return statement.
 400  i :    return false;
 401  E :  }
 402    :  
 403    :  }  // namespace trace::client
 404    :  }  // namespace trace

Coverage information generated Thu Mar 14 11:53:36 2013.