Coverage for /Syzygy/agent/asan/asan_runtime.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
83.7%1391660.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    :  #include "syzygy/agent/asan/asan_runtime.h"
  16    :  
  17    :  #include "base/bind.h"
  18    :  #include "base/command_line.h"
  19    :  #include "base/environment.h"
  20    :  #include "base/logging.h"
  21    :  #include "base/string_number_conversions.h"
  22    :  #include "base/string_tokenizer.h"
  23    :  #include "base/sys_string_conversions.h"
  24    :  #include "base/utf_string_conversions.h"
  25    :  #include "syzygy/agent/asan/asan_logger.h"
  26    :  #include "syzygy/agent/asan/stack_capture_cache.h"
  27    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  28    :  
  29    :  namespace agent {
  30    :  namespace asan {
  31    :  
  32    :  namespace {
  33    :  
  34    :  using agent::asan::AsanLogger;
  35    :  using agent::asan::HeapProxy;
  36    :  using agent::asan::StackCaptureCache;
  37    :  
  38    :  // The default error handler.
  39    :  // @param context The context when the error has been reported.
  40    :  // @param stack_id The id of the crash stack trace.
  41  i :  void OnAsanError(CONTEXT* context) {
  42  i :    ::DebugBreak();
  43  i :    ::RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
  44  i :  }
  45    :  
  46    :  // Try to update the value of a size_t variable from a command-line.
  47    :  // @param cmd_line The command line who might contain a given parameter.
  48    :  // @param param_name The parameter that we want to read.
  49    :  // @param value Will receive the value of the parameter if it's present.
  50    :  // @returns true on success, false otherwise.
  51    :  bool UpdateSizetFromCommandLine(const CommandLine& cmd_line,
  52    :                                  const std::string& param_name,
  53  E :                                  size_t* value) {
  54  E :    DCHECK(value != NULL);
  55  E :    if (!cmd_line.HasSwitch(param_name))
  56  E :      return true;
  57  E :    std::string value_str = cmd_line.GetSwitchValueASCII(param_name);
  58  E :    size_t new_value = 0;
  59  E :    if (!base::StringToSizeT(value_str, &new_value))
  60  i :      return false;
  61  E :    *value = new_value;
  62    :  
  63  E :    return true;
  64  E :  }
  65    :  
  66    :  // Try to update the value of an array of ignored stack ids from a command-line.
  67    :  // We expect the values to be in hexadecimal format and separated by a
  68    :  // semi-colon.
  69    :  // @param cmd_line The command line to parse.
  70    :  // @param param_name The parameter that we want to read.
  71    :  // @param values Will receive the set of parsed values.
  72    :  // @returns true on success, false otherwise.
  73    :  bool ReadIgnoredStackIdsFromCommandLine(const CommandLine& cmd_line,
  74    :                                          const std::string& param_name,
  75  E :                                          AsanRuntime::StackIdSet* values) {
  76  E :    DCHECK(values != NULL);
  77  E :    if (!cmd_line.HasSwitch(param_name))
  78  E :      return true;
  79  E :    std::string value_str = cmd_line.GetSwitchValueASCII(param_name);
  80  E :    StringTokenizer string_tokenizer(value_str, ";");
  81  E :    while (string_tokenizer.GetNext()) {
  82  E :      int new_value = 0;
  83  E :      if (!base::HexStringToInt(string_tokenizer.token(), &new_value))
  84  i :        return false;
  85  E :      values->insert(static_cast<StackCapture::StackId>(new_value));
  86  E :    }
  87  E :    return true;
  88  E :  }
  89    :  
  90    :  // A helper function to find if an intrusive list contains a given entry.
  91    :  // @param list The list in which we want to look for the entry.
  92    :  // @param item The entry we want to look for.
  93    :  // @returns true if the list contains this entry, false otherwise.
  94  E :  bool HeapListContainsEntry(const LIST_ENTRY* list, const LIST_ENTRY* item) {
  95  E :    LIST_ENTRY* current = list->Flink;
  96  E :    while (current != NULL) {
  97  E :      LIST_ENTRY* next_item = NULL;
  98  E :      if (current->Flink != list) {
  99  E :        next_item = current->Flink;
 100    :      }
 101    :  
 102  E :      if (current == item) {
 103  E :        return true;
 104    :      }
 105    :  
 106  E :      current = next_item;
 107  E :    }
 108  i :    return false;
 109  E :  }
 110    :  
 111    :  }  // namespace
 112    :  
 113    :  const char AsanRuntime::kSyzyAsanEnvVar[] = "ASAN_OPTIONS";
 114    :  
 115    :  const char AsanRuntime::kBottomFramesToSkip[] =
 116    :      "bottom_frames_to_skip";
 117    :  const char AsanRuntime::kQuarantineSize[] = "quarantine_size";
 118    :  const char AsanRuntime::kCompressionReportingPeriod[] =
 119    :      "compression_reporting_period";
 120    :  const char AsanRuntime::kMaxNumberOfFrames[] = "max_num_frames";
 121    :  const char AsanRuntime::kIgnoredStackIds[] = "ignored_stack_ids";
 122    :  const wchar_t AsanRuntime::kSyzyAsanDll[] = L"asan_rtl.dll";
 123    :  
 124  E :  AsanRuntime::AsanRuntime() : logger_(NULL), stack_cache_(NULL) {
 125  E :  }
 126    :  
 127  E :  AsanRuntime::~AsanRuntime() {
 128  E :  }
 129    :  
 130  E :  void AsanRuntime::SetUp(const std::wstring& flags_command_line) {
 131    :    // Initialize the command-line structures. This is needed so that
 132    :    // SetUpLogger() can include the command-line in the message announcing
 133    :    // this process. Note: this is mostly for debugging purposes.
 134  E :    CommandLine::Init(0, NULL);
 135    :  
 136  E :    InitializeListHead(&heap_proxy_dlist_);
 137    :  
 138    :    // Setup the "global" state.
 139  E :    SetUpLogger();
 140  E :    SetUpStackCache();
 141  E :    if (!ParseFlagsFromString(flags_command_line)) {
 142  i :      LOG(ERROR) << "Unable to parse the flags from the input string (\""
 143    :                 << flags_command_line.c_str() << "\").";
 144    :    }
 145    :    // Propagates the flags values to the different modules.
 146  E :    PropagateFlagsValues();
 147    :  
 148    :    // Use the default callback.
 149  E :    SetErrorCallBack(&OnAsanError);
 150  E :  }
 151    :  
 152  E :  void AsanRuntime::TearDown() {
 153  E :    TearDownStackCache();
 154  E :    TearDownLogger();
 155  E :    DCHECK(asan_error_callback_.is_null() == FALSE);
 156  E :    asan_error_callback_.Reset();
 157    :    // In principle, we should also check that all the heaps have been destroyed
 158    :    // but this is not guaranteed to be the case in Chrome, so the heap list may
 159    :    // not be empty here.
 160  E :  }
 161    :  
 162  E :  void AsanRuntime::OnError(CONTEXT* context) {
 163  E :    DCHECK(context != NULL);
 164    :    // Call the callback to handle this error.
 165  E :    DCHECK_EQ(false, asan_error_callback_.is_null());
 166  E :    asan_error_callback_.Run(context);
 167  E :  }
 168    :  
 169  E :  void AsanRuntime::SetErrorCallBack(void (*callback)(CONTEXT*)) {
 170  E :    asan_error_callback_ = base::Bind(callback);
 171  E :  }
 172    :  
 173  E :  void AsanRuntime::SetUpLogger() {
 174    :    // Setup variables we're going to use.
 175  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 176  E :    scoped_ptr<AsanLogger> client(new AsanLogger);
 177  E :    CHECK(env.get() != NULL);
 178  E :    CHECK(client.get() != NULL);
 179    :  
 180    :    // Initialize the client.
 181  E :    std::string instance_id;
 182  E :    if (env->GetVar(kSyzygyRpcInstanceIdEnvVar, &instance_id))
 183  E :      client->set_instance_id(UTF8ToWide(instance_id));
 184  E :    client->Init();
 185    :  
 186    :    // Register the client singleton instance.
 187  E :    logger_.reset(client.release());
 188  E :  }
 189    :  
 190  E :  void AsanRuntime::TearDownLogger() {
 191  E :    logger_.reset();
 192  E :  }
 193    :  
 194  E :  void AsanRuntime::SetUpStackCache() {
 195  E :    DCHECK(stack_cache_.get() == NULL);
 196  E :    DCHECK(logger_.get() != NULL);
 197  E :    stack_cache_.reset(new StackCaptureCache(logger_.get()));
 198  E :  }
 199    :  
 200  E :  void AsanRuntime::TearDownStackCache() {
 201  E :    DCHECK(stack_cache_.get() != NULL);
 202  E :    stack_cache_->LogCompressionRatio();
 203  E :    stack_cache_.reset();
 204  E :  }
 205    :  
 206  E :  bool AsanRuntime::ParseFlagsFromString(std::wstring str) {
 207    :    // Prepends the flags with the agent name. We need to do this because the
 208    :    // command-line constructor expect the process name to be the first value of
 209    :    // the command-line string.
 210    :    // Start by inserting a space at the beginning of the flags to separate the
 211    :    // flags from the agent name.
 212  E :    str.insert(0, L" ");
 213    :    // Insert the agent name.
 214  E :    str.insert(0, kSyzyAsanDll);
 215    :  
 216  E :    CommandLine cmd_line = CommandLine::FromString(str);
 217    :  
 218    :    // Parse the quarantine size flag.
 219  E :    flags_.quarantine_size = HeapProxy::default_quarantine_max_size();
 220    :    if (!UpdateSizetFromCommandLine(cmd_line, kQuarantineSize,
 221  E :                                    &flags_.quarantine_size)) {
 222  i :      LOG(ERROR) << "Unable to read " << kQuarantineSize << " from the argument "
 223    :                 << "list.";
 224  i :      return false;
 225    :    }
 226    :  
 227    :    // Parse the reporting period flag.
 228    :    flags_.reporting_period =
 229  E :        StackCaptureCache::GetDefaultCompressionReportingPeriod();
 230    :    if (!UpdateSizetFromCommandLine(cmd_line, kCompressionReportingPeriod,
 231  E :                                    &flags_.reporting_period)) {
 232  i :      LOG(ERROR) << "Unable to read " << kCompressionReportingPeriod
 233    :                 << " from the argument list.";
 234  i :      return false;
 235    :    }
 236    :  
 237    :    // Parse the bottom frames to skip flag.
 238  E :    flags_.bottom_frames_to_skip = StackCapture::bottom_frames_to_skip();
 239    :    if (!UpdateSizetFromCommandLine(cmd_line, kBottomFramesToSkip,
 240  E :                                    &flags_.bottom_frames_to_skip)) {
 241  i :      LOG(ERROR) << "Unable to read " << kBottomFramesToSkip << " from the "
 242    :                 << "argument list.";
 243  i :      return false;
 244    :    }
 245    :  
 246    :    // Parse the max number of frames flag.
 247  E :    flags_.max_num_frames = stack_cache_->max_num_frames();
 248    :    if (!UpdateSizetFromCommandLine(cmd_line, kMaxNumberOfFrames,
 249  E :                                    &flags_.max_num_frames)) {
 250  i :      LOG(ERROR) << "Unable to read " << kMaxNumberOfFrames << " from the "
 251    :                 << "argument list.";
 252  i :      return false;
 253    :    }
 254    :  
 255    :    // Parse the ignored stack ids.
 256    :    if (!ReadIgnoredStackIdsFromCommandLine(cmd_line, kIgnoredStackIds,
 257  E :                                            &flags_.ignored_stack_ids)) {
 258  i :      LOG(ERROR) << "Unable to read " << kIgnoredStackIds << " from the "
 259    :                 << "argument list.";
 260  i :      return false;
 261    :    }
 262    :  
 263  E :    return true;
 264  E :  }
 265    :  
 266  E :  bool AsanRuntime::GetAsanFlagsEnvVar(std::wstring* env_var_wstr) {
 267  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 268  E :    if (env.get() == NULL) {
 269  i :      LOG(ERROR) << "base::Environment::Create returned NULL.";
 270  i :      return false;
 271    :    }
 272    :  
 273    :    // If this fails, the environment variable simply does not exist.
 274  E :    std::string env_var_str;
 275  E :    if (!env->GetVar(kSyzyAsanEnvVar, &env_var_str)) {
 276  E :      return true;
 277    :    }
 278    :  
 279  i :    *env_var_wstr = base::SysUTF8ToWide(env_var_str);
 280    :  
 281  i :    return true;
 282  E :  }
 283    :  
 284  E :  void AsanRuntime::PropagateFlagsValues() const {
 285    :    // TODO(sebmarchand): Look into edit-free ways to expose new flags to the
 286    :    //     different modules.
 287  E :    HeapProxy::set_default_quarantine_max_size(flags_.quarantine_size);
 288  E :    StackCapture::set_bottom_frames_to_skip(flags_.bottom_frames_to_skip);
 289  E :    StackCaptureCache::set_compression_reporting_period(flags_.reporting_period);
 290  E :    stack_cache_->set_max_num_frames(flags_.max_num_frames);
 291  E :  }
 292    :  
 293  E :  void AsanRuntime::set_flags(const AsanFlags* flags) {
 294  E :    DCHECK(flags != NULL);
 295  E :    flags_ = *flags;
 296  E :  }
 297    :  
 298  E :  void AsanRuntime::AddHeap(HeapProxy* heap) {
 299  E :    base::AutoLock lock(heap_proxy_dlist_lock_);
 300  E :    InsertTailList(&heap_proxy_dlist_, HeapProxy::ToListEntry(heap));
 301  E :  }
 302    :  
 303  E :  void AsanRuntime::RemoveHeap(HeapProxy* heap) {
 304  E :    base::AutoLock lock(heap_proxy_dlist_lock_);
 305    :    DCHECK(HeapListContainsEntry(&heap_proxy_dlist_,
 306  E :                                 HeapProxy::ToListEntry(heap)));
 307  E :    RemoveEntryList(HeapProxy::ToListEntry(heap));
 308  E :  }
 309    :  
 310    :  void AsanRuntime::ReportAsanErrorDetails(const void* addr,
 311    :                                           const CONTEXT& context,
 312    :                                           const StackCapture& stack,
 313    :                                           HeapProxy::AccessMode access_mode,
 314  E :                                           size_t access_size) {
 315  E :    base::AutoLock lock(heap_proxy_dlist_lock_);
 316    :    // Iterates over the HeapProxy list to find the memory block containing this
 317    :    // address. We expect that there is at least one heap proxy extant.
 318  E :    HeapProxy* proxy = NULL;
 319  E :    LIST_ENTRY* item = heap_proxy_dlist_.Flink;
 320  E :    CHECK(item != NULL);
 321  E :    while (item != NULL) {
 322  E :      LIST_ENTRY* next_item = NULL;
 323  E :      if (item->Flink != &heap_proxy_dlist_) {
 324  i :        next_item = item->Flink;
 325    :      }
 326    :  
 327  E :      proxy = HeapProxy::FromListEntry(item);
 328  E :      if (proxy->OnBadAccess(addr, context, stack, access_mode, access_size)) {
 329  E :        break;
 330    :      }
 331    :  
 332  i :      item = next_item;
 333  i :    }
 334    :  
 335    :    // If item is NULL then we went through the list without finding the heap
 336    :    // from which this address was allocated. We can just reuse the logger of
 337    :    // the last heap proxy we saw to report an "unknown" error.
 338  E :    if (item == NULL) {
 339  i :      CHECK(proxy != NULL);
 340    :      proxy->ReportUnknownError(addr, context, stack, access_mode,
 341  i :                                access_size);
 342    :    }
 343  E :  }
 344    :  
 345    :  }  // namespace asan
 346    :  }  // namespace agent

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