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

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

Coverage information generated Fri Jul 29 11:00:21 2016.