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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
84.1%1591890.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/strings/string_number_conversions.h"
  26    :  #include "base/strings/string_split.h"
  27    :  #include "base/strings/utf_string_conversions.h"
  28    :  #include "base/win/pe_image.h"
  29    :  #include "syzygy/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(base::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 :    if ((write_ptr + num_bytes) <= end_ptr)
 202  E :      return true;
 203  i :    return false;
 204  E :  }
 205    :  
 206    :  // Returns true if there's enough space left in the given segment to write
 207    :  // a prefixed record of length num_bytes.
 208  E :  bool TraceFileSegment::CanAllocate(size_t num_bytes) const {
 209  E :    DCHECK(num_bytes != 0);
 210  E :    return CanAllocateRaw(num_bytes + sizeof(RecordPrefix));
 211  E :  }
 212    :  
 213  E :  void FillPrefix(RecordPrefix* prefix, int type, size_t size) {
 214  E :    prefix->size = size;
 215  E :    prefix->version.hi = TRACE_VERSION_HI;
 216  E :    prefix->version.lo = TRACE_VERSION_LO;
 217  E :    prefix->type = static_cast<uint16>(type);
 218  E :    prefix->timestamp = trace::common::GetTsc();
 219  E :  }
 220    :  
 221    :  // Writes the segment header at the top of a segment, updating the bytes
 222    :  // consumed and initializing the segment header structures.
 223  E :  void TraceFileSegment::WriteSegmentHeader(SessionHandle session_handle) {
 224  E :    DCHECK(header == NULL);
 225  E :    DCHECK(write_ptr != NULL);
 226  E :    DCHECK(CanAllocate(sizeof(TraceFileSegmentHeader)));
 227    :  
 228    :    // The trace record allocation will write the record prefix and update
 229    :    // the number of bytes consumed within the buffer.
 230    :  
 231  E :    RecordPrefix* prefix = reinterpret_cast<RecordPrefix*>(write_ptr);
 232    :    FillPrefix(prefix,
 233    :               TraceFileSegmentHeader::kTypeId,
 234  E :               sizeof(TraceFileSegmentHeader));
 235    :  
 236  E :    header = reinterpret_cast<TraceFileSegmentHeader*>(prefix + 1);
 237  E :    header->thread_id = ::GetCurrentThreadId();
 238  E :    header->segment_length = 0;
 239    :  
 240  E :    write_ptr = reinterpret_cast<uint8*>(header + 1);
 241  E :  }
 242    :  
 243    :  void* TraceFileSegment::AllocateTraceRecordImpl(int record_type,
 244  E :                                                  size_t record_size) {
 245  E :    DCHECK(header != NULL);
 246  E :    DCHECK(write_ptr != NULL);
 247  E :    DCHECK(record_size != 0);
 248    :  
 249  E :    const size_t total_size = sizeof(RecordPrefix) + record_size;
 250    :  
 251  E :    DCHECK(CanAllocateRaw(total_size));
 252    :  
 253    :    // Clear the memory we're about to allocate. If this thread gets killed
 254    :    // before it can finish updating the trace record we want the allocated
 255    :    // record to have a somewhat consistent state.
 256  E :    ::memset(write_ptr, 0, total_size);
 257    :  
 258  E :    RecordPrefix* prefix = reinterpret_cast<RecordPrefix*>(write_ptr);
 259  E :    FillPrefix(prefix, record_type, record_size);
 260    :  
 261  E :    write_ptr += total_size;
 262  E :    header->segment_length += total_size;
 263    :  
 264  E :    if (!allocate_callback.is_null())
 265  E :      allocate_callback.Run(record_type, record_size, prefix + 1);
 266    :  
 267  E :    return prefix + 1;
 268  E :  }
 269    :  
 270  E :  bool GetModuleBaseAddress(void* address_in_module, void** module_base) {
 271  E :    DCHECK(address_in_module != NULL);
 272  E :    DCHECK(module_base != NULL);
 273    :  
 274    :    // Get the address of the module. We do this by querying for the allocation
 275    :    // that contains the address of the function we intercepted. This must lie
 276    :    // within the instrumented module, and be part of the single allocation in
 277    :    // which the image of the module lies. The base of the module will be the
 278    :    // base address of the allocation.
 279  E :    MEMORY_BASIC_INFORMATION mem_info = {};
 280  E :    if (::VirtualQuery(address_in_module, &mem_info, sizeof(mem_info)) == 0) {
 281  i :      DWORD error = ::GetLastError();
 282  i :      LOG(ERROR) << "VirtualQuery failed: " << ::common::LogWe(error) << ".";
 283  i :      return false;
 284    :    }
 285    :  
 286  E :    *module_base = mem_info.AllocationBase;
 287    :  
 288    :  #ifndef NDEBUG
 289  E :    base::win::PEImage image(*module_base);
 290  E :    DCHECK(image.VerifyMagic());
 291    :  #endif
 292    :  
 293  E :    return true;
 294  E :  }
 295    :  
 296  E :  bool GetModulePath(void* module_base, base::FilePath* module_path) {
 297  E :    DCHECK(module_base != NULL);
 298  E :    DCHECK(module_path != NULL);
 299    :  
 300    :    wchar_t buffer[1024];
 301    :    if (::GetMappedFileName(::GetCurrentProcess(), module_base, buffer,
 302  E :                            arraysize(buffer)) == 0) {
 303  i :      DWORD error = ::GetLastError();
 304  i :      LOG(ERROR) << "GetMappedFileName failed: " << ::common::LogWe(error) << ".";
 305  i :      return false;
 306    :    }
 307    :  
 308  E :    base::FilePath device_path(buffer);
 309  E :    if (!::common::ConvertDevicePathToDrivePath(device_path, module_path))
 310  i :      return false;
 311    :  
 312  E :    return true;
 313  E :  }
 314    :  
 315  E :  std::string GetInstanceIdForModule(const base::FilePath& module_path) {
 316  E :    std::string id;
 317    :    // We don't care if the search is successful or not.
 318    :    GetModuleValueFromEnvVar(::kSyzygyRpcInstanceIdEnvVar, module_path,
 319  E :                             id, KeepAsString(), &id);
 320  E :    return id;
 321  E :  }
 322    :  
 323  E :  std::string GetInstanceIdForThisModule() {
 324  E :    base::FilePath module_path;
 325  E :    CHECK(GetModulePath(&__ImageBase, &module_path));
 326    :  
 327  E :    std::string instance_id = GetInstanceIdForModule(module_path);
 328    :  
 329  E :    return instance_id;
 330  E :  }
 331    :  
 332  E :  bool IsRpcSessionMandatory(const base::FilePath& module_path) {
 333  E :    int value = 0;
 334    :    if (!GetModuleValueFromEnvVar(kSyzygyRpcSessionMandatoryEnvVar, module_path,
 335  E :                                  value, ToInt(), &value)) {
 336  E :      return false;
 337    :    }
 338    :  
 339  E :    if (value == 0)
 340  E :      return false;
 341    :  
 342    :    // Anything non-zero is treated as 'true'.
 343  E :    return true;
 344  E :  }
 345    :  
 346  E :  bool IsRpcSessionMandatoryForThisModule() {
 347  E :    base::FilePath module_path;
 348  E :    CHECK(GetModulePath(&__ImageBase, &module_path));
 349    :  
 350  E :    if (IsRpcSessionMandatory(module_path))
 351  E :      return true;
 352    :  
 353  E :    return false;
 354  E :  }
 355    :  
 356  E :  bool InitializeRpcSession(RpcSession* rpc_session, TraceFileSegment* segment) {
 357  E :    DCHECK(rpc_session != NULL);
 358    :  
 359  E :    std::string id = trace::client::GetInstanceIdForThisModule();
 360  E :    rpc_session->set_instance_id(base::UTF8ToWide(id));
 361  E :    if (rpc_session->CreateSession(segment))
 362  E :      return true;
 363    :  
 364    :    // If the session is not mandatory then return and indicate that we failed
 365    :    // to initialize properly.
 366  E :    if (!IsRpcSessionMandatoryForThisModule())
 367  E :      return false;
 368    :  
 369    :    // If you're seeing this error message it's because the process was unable
 370    :    // to initialize an RPC session, and the state of the
 371    :    // SYZYGY_RPC_SESSION_MANDATORY environment variable indicated that it was
 372    :    // required. Make sure the call-trace service is running with the appropriate
 373    :    // instance ID!
 374  i :    LOG(ERROR) << "RPC session is mandatory, but unable to be created.";
 375    :  
 376    :    // Dump some context regarding the decision to abort.
 377  i :    base::FilePath module_path;
 378  i :    if (GetModulePath(&__ImageBase, &module_path))
 379  i :      LOG(ERROR) << "Module path: " << module_path.value();
 380    :  
 381  i :    LOG(ERROR) << "RPC instance ID is \"" << id << "\".";
 382    :  
 383  i :    scoped_ptr<base::Environment> env(base::Environment::Create());
 384  i :    DCHECK_NE(static_cast<base::Environment*>(nullptr), env.get());
 385  i :    std::string var;
 386  i :    if (env->GetVar(::kSyzygyRpcInstanceIdEnvVar, &var)) {
 387  i :      LOG(ERROR) << ::kSyzygyRpcInstanceIdEnvVar << " is \"" << var << "\".";
 388  i :    } else {
 389  i :      LOG(ERROR) << ::kSyzygyRpcInstanceIdEnvVar << " is not set.";
 390    :    }
 391    :  
 392  i :    if (env->GetVar(::kSyzygyRpcSessionMandatoryEnvVar, &var)) {
 393  i :      LOG(ERROR) << ::kSyzygyRpcSessionMandatoryEnvVar << " is \"" << var
 394    :                  << "\".";
 395  i :    } else {
 396  i :      LOG(ERROR) << ::kSyzygyRpcSessionMandatoryEnvVar << " is not set.";
 397    :    }
 398    :  
 399    :    // Kill this process with prejudice. We need to be heavy handed here because
 400    :    // we are typically running under the loader lock, and most things won't
 401    :    // actually convince it to stop the entire process.
 402  i :    ::TerminateProcess(::GetCurrentProcess(), 255);
 403    :  
 404    :    // We need this to avoid getting complaints about control paths missing a
 405    :    // return statement.
 406  i :    return false;
 407  E :  }
 408    :  
 409    :  }  // namespace client
 410    :  }  // namespace trace

Coverage information generated Thu Mar 26 16:15:41 2015.