月度归档:2013年05月

程序分析工具问题一二则

最近一直被深受程序分析工具的为你困扰,揪出一两个来总结终结。
1.你还在为需要更改代码来进行程序分析而蛋疼无比么?
在偶眼里程序分析工具通常都有两种工作模式:内部模式外部模式
a) 前者需要在待分析代码中植入分析工具的api;后者需要预加载动态库和设置环境变量。
b) 前者优点是可以做局部分析,缺点是需要变更代码;后者优点是不用动程序代码,缺点是只能对整个程序进行分析(局部分析需要对结果做处理,这在代码量级特别大的程序中比较麻烦)。
通常情况下我们选择后者能够更低门槛的进行程序分析。google-perftools就是如此,perf更是原生就用外部模式分析。

【google-perftools】
这个库包括heap checker,heap profiler, cpu profiler.
1).heap checker – heap checker文档

env HEAPCHECK=[normal|strict] ./myprogram_with_tcmalloc
env LD_PRELOAD=libtcmalloc.so HEAPCHECK=[normal|strict] ./myprogram_without_tcmalloc

2).heap profiler – heap profiler文档

env HEAPPROFILE=./memprof ./myprogram_with_tcmalloc
env LD_PRELOAD=libtcmalloc.so HEAPPROFILE=./memprof ./myprogram_without_tcmalloc

3).cpu profiler – cpu profiler文档

env CPUPROFILE=./cpuprof [CPUPROFILE_FREQUENCY=100] ./myprogram_with_profiler
env LD_PRELOAD=libprofiler.so CPUPROFILE=./cpuprof [CPUPROFILE_FREQUENCY=100] ./myprogram_without_profiler

【perf】

perf stat ./myprogram 初略看看程序运行的概况
perf top 查询运行过程中,执行情况
perf record -e cpu-clock -g ./myprogram 分析程序热点
perf report 生成报告

2.你还在为分析工具不能正常生成分析文件而纠结不堪么?
相信很多人都会遇到工具失效的情况,例如perf和gperf等工具生成不了结果文件,其根本原因还是代码没有正常退出(显示或隐式的调用return, exit, _exit, _Exit, pthreade_exit)。很多后台服务程序开发为了简便没有对信号进行处理,而很多信号的默认处理方法就是让进程直接退出,这类进程异常终止(abort, signal, pthread_cancel)会导致生成不了分析文件。最直接的方法就是更改代码,添加对异常终止路径的截获然后转到正常终止逻辑,但这样处理起来比较麻烦,使用gcc的一个全局构造特性__attribute__ ((constructor))在程序运行前预加载即可不用更改代码进行程序分析。


gcc exit.c -fPIC -shared -o libexit.so
LD_PRELOAD=libexit.so ./myprogram

这样就会截获指定信号然后转入正常终止的逻辑。

一个chrome工具插件

后台开发中经常需要进行一些转换,例如时间戳整型和日期字符串的转换,ip整型和ip字符串的转换。被弄烦了之后就让maple童鞋整个了页面工具,极大的提高了偶的工作效率,唯一的一点不便就是需要保存这个网页和js文件,索性整成个chrome插件一劳永逸。

插件图片

github下载地址

唯一需要注意的是目前版本chrome插件中的事件绑定不许放到html页面内

细看glog

【基本状况】
1. glog只维护四种级别的日志(log_serverity.h定义 GLOG_INFO = 0, GLOG_WARNING = 1, GLOG_ERROR = 2, GLOG_FATAL = 3)
2. 默认文件名样式: “进程名.机器名.用户名.log.日志级别.日期-时间.进程名”
3. glog支持条件日志,特定场景很有用
4. glog支持模块日志(-vmodule=xxx=yy),非常有用,可以限定到那个文件的那个级别输出
5. 程序core时输出堆栈上下文,这个调试非常有用
6. 没有初始化时日志统统打印到stderr终端
7. 充分利用临时对象,锁是对象销毁时自动解锁,LogMessage是对象析构时自动flush触发真正的写日志

【类】
存放在头文件中,方便外部使用和扩展
– LogMessage 管理写日志信息,glog的基本语法LOG()就是对他的宏定义{内部友员包含LogDestination}
– LogSink 用于添加新的写入后端,glog扩展性的体现
– LogStream 是对std::ostrstream的扩展, 使用其<<符号

– Logger 真正写日志的类(可以在LogDestination中设置)
– LogFileObject 管理日志文件命名/轮转/写入等 LogDestination 日志的后端(LogToSinks,LogToAllLogfiles,MaybeLogToStderr,MaybeLogToLogfile),内部友员包含LogMessage

【LOG(INFO)的演变】
LOG(INFO)
->
#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
->
#define COMPACT_GOOGLE_LOG_INFO @ac_google_namespace@::LogMessage(__FILE, __LINE__)
->
Init(file, line, GLOG_INFO, &LogMessage::SendToLog)
->
LogMessage::~LogMessage()

【特化事例】

  1. 增加新后端
  2. 继承google::LogSink,最重要的实现这个send接口,然后用google::AddLogSink(sink子类)将sink添加到glog后端,自此LOG(INFO)写日志时就会同时写到新添加的后端。

  3. 写日志到特定的后端
  4. 由于某些需要,你可能并不希望将日志写到所有后端(这里会写到所有后端,包括Files,stderr,Email, Sinks)
    a) 仅仅写日志到sink
    glog已经有这个宏了,LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity)
    b) 仅仅写日志到文件
    目前glog没有相应的宏,只得修改源码,在不修改现有接口和功能的原则,新增代码:
    – 给LogMessage添加一个SendToFile()的成员函数()
    – 借用现有的构造函数LogMesage构造宏

  5. 改变写日志的方式
  6. 以下是glog的官方Issue 54中commitor给出的一种在cgi中写日志的方法。通过定义自己的Logger,然后将不同级别的执行流绑定到自己的Logger类上就可以控制写入的形态。

  7. 崩溃时写日志
  8. 程序崩溃时追加上下文堆栈信息到日志文件(因为程序崩溃时需要根据core文件去查堆栈信息,有时候core由于某些原因不会生成)