Google之Chromium浏览器源码学习——base公共通用库(四)

时间:2021-03-23 07:33:42

  本文将介绍debug调试相关的内容,包括调试器、性能分析、堆跟踪、跟踪事件等;

  alias.h:Alias函数,提供防止载微软的编译器优化某参数变量的操作,内部通过#pragma optimize("", off)与#pragma optimize("", on)来实现关闭所有的优化选项,再恢复它们到原始(或默认)的设定;事实上Alias内部未实现任何的操作。

  stack_trace.h:

  EnableInProcessStackDumping: 开启堆栈转储于控制台输出,当可用时进程将被立即停止,仅用于单元测试中且因非线程安全,仅主线调用。针对windows版本,通过调用SetUnhandledExceptionFilter来设置异常捕获函数StackDumpExceptionFilter,其内部为重定向至控制台输出;

  StackTrace:堆栈轨迹类,堆栈轨迹假如你需要打印出某个时间的调用堆栈状态,对象创建位置等,

  首先介绍私有成员变量:

  1. kMaxTraces, trace_[kMaxTraces]:堆栈踪迹最大缓冲区大小, MSDN介绍kMaxTraces应小于63,故内部取值最大为62;

  2. count_:实际有效踪迹帧数;

  3. 不带参数的构造函数StackTrace,内部调用CaptureStackBackTrace,使用当前的调用状态下的堆栈;

  4. 带参构造函数StackTrace,重载了两个版本;其中StackTrace(const void* const* trace, size_t count),通过一个已存在的堆栈轨迹指针地址创建,此外针对windows版本的还提供了StackTrace(_EXCEPTION_POINTERS* exception_pointers),由一个异常对象创建,并调用StackWalk64获取堆栈轨迹信息,内部针对32位和64位实现;

  5. Addresses:获取当前调用堆栈轨迹信息地址;

  6. PrintBacktrace、OutputToStream:打印堆栈轨迹信息至stderr标准错误输出流;

  7. ToString:获取当前堆栈轨迹、符号信息字符串;

  debugger.h:

  1. SpawnDebuggerOnProcess:提供开始注册系统级的JIT即时调试器,并将其附加到指定的进程中;可以看到内部提供跨平台的版本,微软和posix版本;微软版本通过访问注册表HKEY_LOCAL_MACHINE下SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug中的键为Debugger的值,如"C:\windows\system32\vsjitdebugger.exe" -p %ld -e %ld,其中vsjitdebugger.exe为微软的VS即时调试器,参数-p %ld为将要调试的进程ID,-e %ld为可执行程序的进程ID,实际上可以指定一样的进程ID值即可,此后创建新的进程开启该调试;posix版本调用系统函数system,参数xterm -e 'gdb --pid=%u' &,也为进程ID,gdb调试工具。

  2. WaitForDebugger:等待指定的时间秒wait_seconds则返回,或设置参数silent为false时,则若调试器检查到抛出异常则进入调试;内部采用for循环次数为至多10*wait_seconds次,每次等待100毫秒,刚好wait_seconds秒,并循环检查BeingDebugged(若指定进程被attach运行于调试器时,返回值为真);

  3. BeingDebugged:提供不同版本,微软版本通过IsDebuggerPresent函数判断(判断调用进程是否由用户模式的调试器调试,实际上);其他版本细分为linux、BSD、ANDROID等版本,暂不细说明;

  4. BreakDebugger:提供不同版本,微软版本通过__debugbreak,暂停程序执行,打开调试器,进入调试模式;

  5. SetSuppressDebugUI,IsDebugUISuppressed目前用在UI界面是否测试设置的操作。

  debug_on_start_win.h:

  1. 提供了一个在开启运行程序时刻支持调试的类,仅提供Init,FindArgument静态接口实现;通过命令行的方式调试,至多等待时间60s进入调试;而对于命令行调试进程则等待被调试调用。目前其他地方或项目项目暂未使用到该类提供的功能。

  crash_logging.h:主要是提供一些用来添加一些元数据信息至上传负载,并将崩溃信息报告发送至崩溃服务器

  1. ScopedCrashKey:一个封装崩溃键的范围器,不允许赋值构造和复制拷贝,主要用来设置某对象的指定键的值并管理键值对的生命期,析构时清除该对象;内部调用SetCrashKeyValue(key, value)、ClearCrashKey(key)。

  2. CrashKey:用来被注册使用的,提供参数key_name作为崩溃键值名,max_length指定键值最大长度,若超过该长度则被截断;此外若该键值长度超过“chunk_max_length”但小于max_length,则会被拆分为多个块。

  3. g_crash_keys_:std::map<base::StringPiece, CrashKey>类型的全局变量指针,作为崩溃键名字的入口,分别为键与崩溃键值名;

  4. g_chunk_max_length_:单个块的最大长度;

  5. g_set_key_func_:用来设置键值对的函数;

  6. g_clear_key_func_:用来清理键值对的函数;

  7. kLargestValueAllowed:最大的max_length允许长度,1024字节;

  8. LookupCrashKey:查找g_crash_keys_中指定键的CrashKey;

  9. ChunkCrashKeyValue:辅助函数,主要用来给指定的键值名分块;内部现实为:直接截取value的crash_key.max_length长度的值,并按照chunk_max_length对截取的值分块保存至std::vector<std::string>中;

  10. SetCrashKeyValue:通过LookupCrashKey查找到崩溃键值名,若键值名不存在或键值名长度小于g_chunk_max_length_,则调用g_set_key_func_设置崩溃元数据中指定的键值对;否则对键值名截断并拆分为多个块,对于多余的,将通过g_clear_key_func_调用,各个块则调用g_set_key_func_设置块键。

  11. ClearCrashKey:清除g_crash_keys_中指定的崩溃键值名,处理的方式类似于SetCrashKeyValue,内部改为调用g_clear_key_func_实现清理工作;

  12. SetCrashKeyToStackTrace:将记录的堆栈轨迹信息写入知道的崩溃键值名crash Key中;

  13. SetCrashKeyFromAddresses:针对12,分别取出记录的堆栈信息并按照空格隔开写入崩溃键值名中;

  14. InitCrashKeys:在使用崩溃键记录前需要调用的,所有需要用到的崩溃记录键都需要被注册,注册到g_crash_keys_,参数keys为崩溃键数组,count为最大记录数,chunk_max_length为单个块最大长度;

  15. ResetCrashLoggingForTesting:重置崩溃键系统,可被重新初始化(再次调用InitCrashKeys),一般用在测试中。  

  使用示例;

  

     base::debug::StackTrace trace;
std::ostringstream os;
trace.OutputToStream(&os);
std::string backtrace_message = os.str();
std::cout << backtrace_message << std::endl;
std::cout << trace.ToString() << std::endl;
size_t frames_found = ;
trace.Addresses(&frames_found); {
base::debug::StackTrace trace;
std::ostringstream os;
trace.OutputToStream(&os);
std::cout << os.str() << std::endl;
}
 std::map<std::string, std::string>* key_values_ = NULL;

 class CrashLoggingTest
{
public:
CrashLoggingTest()
{
key_values_ = new std::map<std::string, std::string>;
base::debug::SetCrashKeyReportingFunctions(
&CrashLoggingTest::SetKeyValue,
&CrashLoggingTest::ClearKeyValue);
} virtual ~CrashLoggingTest()
{
base::debug::ResetCrashLoggingForTesting(); delete key_values_;
key_values_ = NULL;
} private: static void SetKeyValue(const base::StringPiece& key,
const base::StringPiece& value)
{
(*key_values_)[key.as_string()] = value.as_string();
} static void ClearKeyValue(const base::StringPiece& key)
{
key_values_->erase(key.as_string());
}
}; CrashLoggingTest crashLog; const char* kTestKey = "test-key";
base::debug::CrashKey keys[] = { { kTestKey, } };
base::debug::InitCrashKeys(keys, arraysize(keys), ); base::debug::SetCrashKeyValue(kTestKey, "value");
bool res = false;
res = "value" == (*key_values_)[kTestKey]; base::debug::ClearCrashKey(kTestKey);
res = (key_values_->end() == key_values_->find(kTestKey)); key_values_->clear();
delete key_values_;

  leak_tracker.h:泄露跟踪者,一个用来验证一个类的所有实例是否被安全销毁的助手,在单线程使用中比较有用,若有泄露,则每个实例的内存分配将会被写入日志;在使用时ENABLE_LEAK_TRACKER宏需要定义,否则将无效且不会记录调用堆栈信息。

   使用方式比较简单,如:

  

 class A
{
// ...
private:
base::LeakTracker<A> leak_tracker_;
};

  此后通过调用 LeakTracker<A>::CheckForLeaks()来检查是否所有实例被销毁;对于失败时,有泄露,则每个实例的内存分配将会被写入日志。

  事实上,日志记录堆栈信息,内部采用StackTrace allocation_stack_成员记录堆栈踪迹;

  LeakTracker:模板类,继承于LinkNode,即之前我们接触的堆栈列表容器,如:template<typename T> class LeakTracker : public LinkNode<LeakTracker<T> > ;

  首先,成员变量allocation_stack_,用来记录堆栈踪迹;

  静态成员函数:

  1. instances:生成对应模板类型的static LinkedList<LeakTracker<T> > list实例,它主要用来保存所有创建的实例(实际上是保存该实例下的成员变量leak_tracker_,来达到记录实例数和是否存在的状态);

  2. CheckForLeaks:用来检测当前是否所有实例已被销毁,否则打印实例内存分配堆栈踪迹信息至日志文件,实时上针对每个实例内部只打印了三条堆栈踪迹;

  3. NumLiveInstances:得到当前存活的实例数目;

  构造函数LeakTracker:内部list->append(this),追加当前实例至静态变量list中;析构函数~LeakTracker:通过this->RemoveFromList()从list中移除本对象实例;

  使用示例:

  

 class ClassA
{
private:
LeakTracker<ClassA> leak_tracker_;
}; bool res = false;
int num = LeakTracker<ClassA>::NumLiveInstances(); // num == -1; ClassA a1;
num = LeakTracker<ClassA>::NumLiveInstances(); // num == 1
scoped_ptr<ClassA> a2(new ClassA);
num = LeakTracker<ClassA>::NumLiveInstances(); // num == 2
a2.reset();
num = LeakTracker<ClassA>::NumLiveInstances(); // num == 1 LeakTracker<ClassA>::CheckForLeaks();// leaks!!!

  总结:因其继承于LinkNode,之前在讨论容器部分时对LinkNode有说明,其限制条件和LeakTracker一致的:不要用相同的指针对象。