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

Coverage information generated Thu Jul 04 09:34:53 2013.