本文将介绍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:windowssystem32vsjitdebugger.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),一般用在测试中。
使用示例;
1 base::debug::StackTrace trace; 2 std::ostringstream os; 3 trace.OutputToStream(&os); 4 std::string backtrace_message = os.str(); 5 std::cout << backtrace_message << std::endl; 6 std::cout << trace.ToString() << std::endl; 7 size_t frames_found = 0; 8 trace.Addresses(&frames_found); 9 10 { 11 base::debug::StackTrace trace; 12 std::ostringstream os; 13 trace.OutputToStream(&os); 14 std::cout << os.str() << std::endl; 15 }
1 std::map<std::string, std::string>* key_values_ = NULL; 2 3 class CrashLoggingTest 4 { 5 public: 6 CrashLoggingTest() 7 { 8 key_values_ = new std::map<std::string, std::string>; 9 base::debug::SetCrashKeyReportingFunctions( 10 &CrashLoggingTest::SetKeyValue, 11 &CrashLoggingTest::ClearKeyValue); 12 } 13 14 virtual ~CrashLoggingTest() 15 { 16 base::debug::ResetCrashLoggingForTesting(); 17 18 delete key_values_; 19 key_values_ = NULL; 20 } 21 22 private: 23 24 static void SetKeyValue(const base::StringPiece& key, 25 const base::StringPiece& value) 26 { 27 (*key_values_)[key.as_string()] = value.as_string(); 28 } 29 30 static void ClearKeyValue(const base::StringPiece& key) 31 { 32 key_values_->erase(key.as_string()); 33 } 34 }; 35 36 CrashLoggingTest crashLog; 37 38 const char* kTestKey = "test-key"; 39 base::debug::CrashKey keys[] = { { kTestKey, 255 } }; 40 base::debug::InitCrashKeys(keys, arraysize(keys), 255); 41 42 base::debug::SetCrashKeyValue(kTestKey, "value"); 43 bool res = false; 44 res = "value" == (*key_values_)[kTestKey]; 45 46 base::debug::ClearCrashKey(kTestKey); 47 res = (key_values_->end() == key_values_->find(kTestKey)); 48 49 key_values_->clear(); 50 delete key_values_;
leak_tracker.h:泄露跟踪者,一个用来验证一个类的所有实例是否被安全销毁的助手,在单线程使用中比较有用,若有泄露,则每个实例的内存分配将会被写入日志;在使用时ENABLE_LEAK_TRACKER宏需要定义,否则将无效且不会记录调用堆栈信息。
使用方式比较简单,如:
1 class A 2 { 3 // ... 4 private: 5 base::LeakTracker<A> leak_tracker_; 6 };
此后通过调用 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中移除本对象实例;
使用示例:
1 class ClassA 2 { 3 private: 4 LeakTracker<ClassA> leak_tracker_; 5 }; 6 7 bool res = false; 8 int num = LeakTracker<ClassA>::NumLiveInstances(); // num == -1; 9 10 ClassA a1; 11 num = LeakTracker<ClassA>::NumLiveInstances(); // num == 1 12 scoped_ptr<ClassA> a2(new ClassA); 13 num = LeakTracker<ClassA>::NumLiveInstances(); // num == 2 14 a2.reset(); 15 num = LeakTracker<ClassA>::NumLiveInstances(); // num == 1 16 17 LeakTracker<ClassA>::CheckForLeaks();// leaks!!!
总结:因其继承于LinkNode,之前在讨论容器部分时对LinkNode有说明,其限制条件和LeakTracker一致的:不要用相同的指针对象。