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_)
     50     return;
     51 
     52   const CrashKey* crash_key = LookupCrashKey(key);
     53 
     54   // TODO(rsesek): Do this:
     55   //DCHECK(crash_key) << "All crash keys must be registered before use "
     56   //                  << "(key = " << key << ")";
     57 
     58   // Handle the un-chunked case.
     59   if (!crash_key || crash_key->max_length <= g_chunk_max_length_) {
     60     g_set_key_func_(key, value);
     61     return;
     62   }
     63 
     64   // Unset the unused chunks.
     65   std::vector<std::string> chunks =
     66       ChunkCrashKeyValue(*crash_key, value, g_chunk_max_length_);
     67   for (size_t i = chunks.size();
     68        i < NumChunksForLength(crash_key->max_length);
     69        ++i) {
     70     g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1));
     71   }
     72 
     73   // Set the chunked keys.
     74   for (size_t i = 0; i < chunks.size(); ++i) {
     75     g_set_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1),
     76                     chunks[i]);
     77   }
     78 }
     79 
     80 void ClearCrashKey(const base::StringPiece& key) {
     81   if (!g_clear_key_func_)
     82     return;
     83 
     84   const CrashKey* crash_key = LookupCrashKey(key);
     85 
     86   // Handle the un-chunked case.
     87   if (!crash_key || crash_key->max_length <= g_chunk_max_length_) {
     88     g_clear_key_func_(key);
     89     return;
     90   }
     91 
     92   for (size_t i = 0; i < NumChunksForLength(crash_key->max_length); ++i) {
     93     g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1));
     94   }
     95 }
     96 
     97 void SetCrashKeyToStackTrace(const base::StringPiece& key,
     98                              const StackTrace& trace) {
     99   size_t count = 0;
    100   const void* const* addresses = trace.Addresses(&count);
    101   SetCrashKeyFromAddresses(key, addresses, count);
    102 }
    103 
    104 void SetCrashKeyFromAddresses(const base::StringPiece& key,
    105                               const void* const* addresses,
    106                               size_t count) {
    107   std::string value = "<null>";
    108   if (addresses && count) {
    109     const size_t kBreakpadValueMax = 255;
    110 
    111     std::vector<std::string> hex_backtrace;
    112     size_t length = 0;
    113 
    114     for (size_t i = 0; i < count; ++i) {
    115       std::string s = base::StringPrintf("%p", addresses[i]);
    116       length += s.length() + 1;
    117       if (length > kBreakpadValueMax)
    118         break;
    119       hex_backtrace.push_back(s);
    120     }
    121 
    122     value = JoinString(hex_backtrace, ' ');
    123 
    124     // Warn if this exceeds the breakpad limits.
    125     DCHECK_LE(value.length(), kBreakpadValueMax);
    126   }
    127 
    128   SetCrashKeyValue(key, value);
    129 }
    130 
    131 ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key,
    132                                const base::StringPiece& value)
    133     : key_(key.as_string()) {
    134   SetCrashKeyValue(key, value);
    135 }
    136 
    137 ScopedCrashKey::~ScopedCrashKey() {
    138   ClearCrashKey(key_);
    139 }
    140 
    141 size_t InitCrashKeys(const CrashKey* const keys, size_t count,
    142                      size_t chunk_max_length) {
    143   DCHECK(!g_crash_keys_) << "Crash logging may only be initialized once";
    144   if (!keys) {
    145     delete g_crash_keys_;
    146     g_crash_keys_ = NULL;
    147     return 0;
    148   }
    149 
    150   g_crash_keys_ = new CrashKeyMap;
    151   g_chunk_max_length_ = chunk_max_length;
    152 
    153   size_t total_keys = 0;
    154   for (size_t i = 0; i < count; ++i) {
    155     g_crash_keys_->insert(std::make_pair(keys[i].key_name, keys[i]));
    156     total_keys += NumChunksForLength(keys[i].max_length);
    157     DCHECK_LT(keys[i].max_length, kLargestValueAllowed);
    158   }
    159   DCHECK_EQ(count, g_crash_keys_->size())
    160       << "Duplicate crash keys were registered";
    161 
    162   return total_keys;
    163 }
    164 
    165 const CrashKey* LookupCrashKey(const base::StringPiece& key) {
    166   CrashKeyMap::const_iterator it = g_crash_keys_->find(key.as_string());
    167   if (it == g_crash_keys_->end())
    168     return NULL;
    169   return &(it->second);
    170 }
    171 
    172 void SetCrashKeyReportingFunctions(
    173     SetCrashKeyValueFuncT set_key_func,
    174     ClearCrashKeyValueFuncT clear_key_func) {
    175   g_set_key_func_ = set_key_func;
    176   g_clear_key_func_ = clear_key_func;
    177 }
    178 
    179 std::vector<std::string> ChunkCrashKeyValue(const CrashKey& crash_key,
    180                                             const base::StringPiece& value,
    181                                             size_t chunk_max_length) {
    182   std::string value_string = value.substr(0, crash_key.max_length).as_string();
    183   std::vector<std::string> chunks;
    184   for (size_t offset = 0; offset < value_string.length(); ) {
    185     std::string chunk = value_string.substr(offset, chunk_max_length);
    186     chunks.push_back(chunk);
    187     offset += chunk.length();
    188   }
    189   return chunks;
    190 }
    191 
    192 void ResetCrashLoggingForTesting() {
    193   delete g_crash_keys_;
    194   g_crash_keys_ = NULL;
    195   g_chunk_max_length_ = 0;
    196   g_set_key_func_ = NULL;
    197   g_clear_key_func_ = NULL;
    198 }
    199 
    200 }  // namespace debug
    201 }  // namespace base
    202