Home | History | Annotate | Download | only in debug
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/debug/crash_logging.h"
      6 
      7 #include <cmath>
      8 #include <map>
      9 
     10 #include "base/debug/stack_trace.h"
     11 #include "base/format_macros.h"
     12 #include "base/logging.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/stringprintf.h"
     15 
     16 namespace base {
     17 namespace debug {
     18 
     19 namespace {
     20 
     21 // Global map of crash key names to registration entries.
     22 typedef std::map<base::StringPiece, CrashKey> CrashKeyMap;
     23 CrashKeyMap* g_crash_keys_ = NULL;
     24 
     25 // The maximum length of a single chunk.
     26 size_t g_chunk_max_length_ = 0;
     27 
     28 // String used to format chunked key names.
     29 const char kChunkFormatString[] = "%s-%" PRIuS;
     30 
     31 // The functions that are called to actually set the key-value pairs in the
     32 // crash reportng system.
     33 SetCrashKeyValueFuncT g_set_key_func_ = NULL;
     34 ClearCrashKeyValueFuncT g_clear_key_func_ = NULL;
     35 
     36 // For a given |length|, computes the number of chunks a value of that size
     37 // will occupy.
     38 size_t NumChunksForLength(size_t length) {
     39   return std::ceil(length / static_cast<float>(g_chunk_max_length_));
     40 }
     41 
     42 // The longest max_length allowed by the system.
     43 const size_t kLargestValueAllowed = 1024;
     44 
     45 }  // namespace
     46 
     47 void SetCrashKeyValue(const base::StringPiece& key,
     48                       const base::StringPiece& value) {
     49   if (!g_set_key_func_ || !g_crash_keys_)
     50     return;
     51 
     52   const CrashKey* crash_key = LookupCrashKey(key);
     53 
     54   DCHECK(crash_key) << "All crash keys must be registered before use "
     55                     << "(key = " << key << ")";
     56 
     57   // Handle the un-chunked case.
     58   if (!crash_key || crash_key->max_length <= g_chunk_max_length_) {
     59     g_set_key_func_(key, value);
     60     return;
     61   }
     62 
     63   // Unset the unused chunks.
     64   std::vector<std::string> chunks =
     65       ChunkCrashKeyValue(*crash_key, value, g_chunk_max_length_);
     66   for (size_t i = chunks.size();
     67        i < NumChunksForLength(crash_key->max_length);
     68        ++i) {
     69     g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1));
     70   }
     71 
     72   // Set the chunked keys.
     73   for (size_t i = 0; i < chunks.size(); ++i) {
     74     g_set_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1),
     75                     chunks[i]);
     76   }
     77 }
     78 
     79 void ClearCrashKey(const base::StringPiece& key) {
     80   if (!g_clear_key_func_ || !g_crash_keys_)
     81     return;
     82 
     83   const CrashKey* crash_key = LookupCrashKey(key);
     84 
     85   // Handle the un-chunked case.
     86   if (!crash_key || crash_key->max_length <= g_chunk_max_length_) {
     87     g_clear_key_func_(key);
     88     return;
     89   }
     90 
     91   for (size_t i = 0; i < NumChunksForLength(crash_key->max_length); ++i) {
     92     g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1));
     93   }
     94 }
     95 
     96 void SetCrashKeyToStackTrace(const base::StringPiece& key,
     97                              const StackTrace& trace) {
     98   size_t count = 0;
     99   const void* const* addresses = trace.Addresses(&count);
    100   SetCrashKeyFromAddresses(key, addresses, count);
    101 }
    102 
    103 void SetCrashKeyFromAddresses(const base::StringPiece& key,
    104                               const void* const* addresses,
    105                               size_t count) {
    106   std::string value = "<null>";
    107   if (addresses && count) {
    108     const size_t kBreakpadValueMax = 255;
    109 
    110     std::vector<std::string> hex_backtrace;
    111     size_t length = 0;
    112 
    113     for (size_t i = 0; i < count; ++i) {
    114       std::string s = base::StringPrintf("%p", addresses[i]);
    115       length += s.length() + 1;
    116       if (length > kBreakpadValueMax)
    117         break;
    118       hex_backtrace.push_back(s);
    119     }
    120 
    121     value = JoinString(hex_backtrace, ' ');
    122 
    123     // Warn if this exceeds the breakpad limits.
    124     DCHECK_LE(value.length(), kBreakpadValueMax);
    125   }
    126 
    127   SetCrashKeyValue(key, value);
    128 }
    129 
    130 ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key,
    131                                const base::StringPiece& value)
    132     : key_(key.as_string()) {
    133   SetCrashKeyValue(key, value);
    134 }
    135 
    136 ScopedCrashKey::~ScopedCrashKey() {
    137   ClearCrashKey(key_);
    138 }
    139 
    140 size_t InitCrashKeys(const CrashKey* const keys, size_t count,
    141                      size_t chunk_max_length) {
    142   DCHECK(!g_crash_keys_) << "Crash logging may only be initialized once";
    143   if (!keys) {
    144     delete g_crash_keys_;
    145     g_crash_keys_ = NULL;
    146     return 0;
    147   }
    148 
    149   g_crash_keys_ = new CrashKeyMap;
    150   g_chunk_max_length_ = chunk_max_length;
    151 
    152   size_t total_keys = 0;
    153   for (size_t i = 0; i < count; ++i) {
    154     g_crash_keys_->insert(std::make_pair(keys[i].key_name, keys[i]));
    155     total_keys += NumChunksForLength(keys[i].max_length);
    156     DCHECK_LT(keys[i].max_length, kLargestValueAllowed);
    157   }
    158   DCHECK_EQ(count, g_crash_keys_->size())
    159       << "Duplicate crash keys were registered";
    160 
    161   return total_keys;
    162 }
    163 
    164 const CrashKey* LookupCrashKey(const base::StringPiece& key) {
    165   if (!g_crash_keys_)
    166     return NULL;
    167   CrashKeyMap::const_iterator it = g_crash_keys_->find(key.as_string());
    168   if (it == g_crash_keys_->end())
    169     return NULL;
    170   return &(it->second);
    171 }
    172 
    173 void SetCrashKeyReportingFunctions(
    174     SetCrashKeyValueFuncT set_key_func,
    175     ClearCrashKeyValueFuncT clear_key_func) {
    176   g_set_key_func_ = set_key_func;
    177   g_clear_key_func_ = clear_key_func;
    178 }
    179 
    180 std::vector<std::string> ChunkCrashKeyValue(const CrashKey& crash_key,
    181                                             const base::StringPiece& value,
    182                                             size_t chunk_max_length) {
    183   std::string value_string = value.substr(0, crash_key.max_length).as_string();
    184   std::vector<std::string> chunks;
    185   for (size_t offset = 0; offset < value_string.length(); ) {
    186     std::string chunk = value_string.substr(offset, chunk_max_length);
    187     chunks.push_back(chunk);
    188     offset += chunk.length();
    189   }
    190   return chunks;
    191 }
    192 
    193 void ResetCrashLoggingForTesting() {
    194   delete g_crash_keys_;
    195   g_crash_keys_ = NULL;
    196   g_chunk_max_length_ = 0;
    197   g_set_key_func_ = NULL;
    198   g_clear_key_func_ = NULL;
    199 }
    200 
    201 }  // namespace debug
    202 }  // namespace base
    203