WebKit代码量较大,很多逻辑非常复杂,光使用断点调试,可能不能更直观看到想要观察的数据。一方面对于一些嵌套非常强的逻辑,例如:递归等,使用断点调试很难直观看到相互关系,WebKit中一个典型的递归应用就是对于render树等树形结构的的遍历,比如我们要打印出一个render树的各个节点,要直观的看到其结构,就需要使用Log另外一方面WebKit中很多对象巨大,变量隐藏很深,比如嵌套5,6个基类,并且还有智能指针的包裹,使得使用vc展开观看非常麻烦,所以传统的log打印对于我们来说仍然需要。 这里介绍webkit log打印部分,工程来自webkit.org下载的vc工程,在windows上编译通过。
可能使用者第一反应就是使用stdio里面的一些printf方法等,还有就是c++的cin,cout等,经笔者尝试都是徒劳的,不能显示出来。进一步研究发现,webkit在wtf里面专门做了打印的相关包装。WTF是一个子工程,其主要为WebKit提供各种基础工具,这里封装了,著名的智能指针,断言,log输出,内存分配管理,甚至重写了一套独立的容器类,比如hashmap,hashSet,Vector等,其中最为重要是智能指针的实现。其输出为\WebKitBuild\lib。
WTF里面Assertions.cpp提供了log输出的功能函数,其包装了vprintf_stderr_common,并且提供了一个功能强大灵活多变的,带有check性质的Log输出函数WTFLog。
WTFLog是一个传统的变参函数,定义如下:
void WTFLog(WTFLogChannel* channel, const char* format, ...)
比较其差别在于第一个参数,channel。该变量作用为可以进行log输出控制等操作。
typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState;
typedef struct {
unsigned mask;
const char *defaultName;
WTFLogChannelState state;
} WTFLogChannel;
这里的WTFLogChannelState,管理是否打印输出。
可能使用者,立马就想到实际调用WTFLog进行打印,但是而WTFLog,该函数没有进行暴露,而是通过Assertions.h进行变化发布出来供大家使用。Assertions.h该文件主要定义了一些assert的一些接口,附带提供了打印输出。
#define LOG(channel, ...)
WTFLog (&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__)
这里定义了LOG宏.该宏调用WETLog.
使用者看到这里可能进一步微笑了,终于发现有头文件暴露出打印接口,但是同时也迷惑于第一个参数channel,不知道干啥的。同时查看下面的定义也一头雾水:&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel)。
发现下面还定义了两个宏:
#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel)
#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel
做一个进一步转化发现,这里第一个参数channel会增加一个preifix,传入到WTFLog函数。
快速的在代码中写下LOG(myChannel,“mytest”);并包含对应头文件。发现编译不过,这里需要定义channel这个变量.webkit大致有两个文件定义channel,当然用户可以自行定义。他们分别是; WebKitLogging.h以及Logging.h.其中WebKitLogging.h主要用于和平台交互相关部分的打印,比如windows部分定义在webkit\win.而logging.h主要负责其他部分的打印。在webview.cpp等和平台交互的基本用webKitLogging.h控制,而webCore里面的基本都用Logging.h来控制。当然还有其他的定义文件,没有关注过。
在Logging.h WebKitLogging.h分别定义了LOG_CHANNEL_PREFIX,如下WebKitLogging.h中的定义:
#define LOG_CHANNEL_PREFIX WebKitLog,因此组合出变量名为WebKitLogXXX,故我们需要定义类似WebKitLogXXX这样的channl变量。刚好下面extern定义了各个模块的log chanel.
extern WTFLogChannel WebKitLogTextInput;
extern WTFLogChannel WebKitLogTiming;等全局变量.
这样我们只要引用了WebKitLogging.h便能使用类似,LOG(myChannel,“mytest”)的打印语句。进而观察WebKitLogging.cpp详细的定义了各个全局变量,如下:
WTFLogChannel WebKitLogTextInput = { 0x00000010, "WebKitLogLevel", WTFLogChannelOff };
WTFLogChannel WebKitLogTiming = { 0x00000020, "WebKitLogLevel", WTFLogChannelOff };
这里的结构体初始化的最后一个参数就是控制log是否打印的开关。如:WTFLogChannelOff 就是控制。
同理 Logging.h也定义了类似的LOG_CHANNEL_PREFIX 以及全局变量,而我们的webCore里面的打印基本都使用的Logging.h。
使用该Log输出方式打印出一个简单的render树如下:
begin to visitor RenderTree
visitor index=0-#document
visitor index=1-HTML
visitor index=2-BODY
visitor index=3-#text
visitor index=3-#text
visitor index=3-BUTTON
visitor index=4 / /匿名标签生成
visitor index=5-#text
visitor index=3-#text
visitor index=3-#text
综上所述总结一下webkit对于log打印的设计思想:
1.WebKit的wtf里面封装了log打印的逻辑,并且以LOG宏的形式提供给外部使用,而真实实现函数WTFLog不暴露而被隐藏掉了。调用则使用宏非常简洁,代码得以优雅。
2.调用者只需要引用WebKitLogging.h或Logging.h,从而回避了去定义channel变量这个过程,也是就说webkit意在隐藏掉WTFLogChannel这个数据结构的存在,而又要能控制log输出开关。
3. 使用channel全局变量进行配置,并在对应.cpp文件中进行修改,使得如果即使修改控制开关,也不会引起全编译。
可能使用者第一反应就是使用stdio里面的一些printf方法等,还有就是c++的cin,cout等,经笔者尝试都是徒劳的,不能显示出来。进一步研究发现,webkit在wtf里面专门做了打印的相关包装。WTF是一个子工程,其主要为WebKit提供各种基础工具,这里封装了,著名的智能指针,断言,log输出,内存分配管理,甚至重写了一套独立的容器类,比如hashmap,hashSet,Vector等,其中最为重要是智能指针的实现。其输出为\WebKitBuild\lib。
WTF里面Assertions.cpp提供了log输出的功能函数,其包装了vprintf_stderr_common,并且提供了一个功能强大灵活多变的,带有check性质的Log输出函数WTFLog。
WTFLog是一个传统的变参函数,定义如下:
void WTFLog(WTFLogChannel* channel, const char* format, ...)
比较其差别在于第一个参数,channel。该变量作用为可以进行log输出控制等操作。
typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState;
typedef struct {
unsigned mask;
const char *defaultName;
WTFLogChannelState state;
} WTFLogChannel;
这里的WTFLogChannelState,管理是否打印输出。
可能使用者,立马就想到实际调用WTFLog进行打印,但是而WTFLog,该函数没有进行暴露,而是通过Assertions.h进行变化发布出来供大家使用。Assertions.h该文件主要定义了一些assert的一些接口,附带提供了打印输出。
#define LOG(channel, ...)
WTFLog (&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__)
这里定义了LOG宏.该宏调用WETLog.
使用者看到这里可能进一步微笑了,终于发现有头文件暴露出打印接口,但是同时也迷惑于第一个参数channel,不知道干啥的。同时查看下面的定义也一头雾水:&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel)。
发现下面还定义了两个宏:
#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel)
#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel
做一个进一步转化发现,这里第一个参数channel会增加一个preifix,传入到WTFLog函数。
快速的在代码中写下LOG(myChannel,“mytest”);并包含对应头文件。发现编译不过,这里需要定义channel这个变量.webkit大致有两个文件定义channel,当然用户可以自行定义。他们分别是; WebKitLogging.h以及Logging.h.其中WebKitLogging.h主要用于和平台交互相关部分的打印,比如windows部分定义在webkit\win.而logging.h主要负责其他部分的打印。在webview.cpp等和平台交互的基本用webKitLogging.h控制,而webCore里面的基本都用Logging.h来控制。当然还有其他的定义文件,没有关注过。
在Logging.h WebKitLogging.h分别定义了LOG_CHANNEL_PREFIX,如下WebKitLogging.h中的定义:
#define LOG_CHANNEL_PREFIX WebKitLog,因此组合出变量名为WebKitLogXXX,故我们需要定义类似WebKitLogXXX这样的channl变量。刚好下面extern定义了各个模块的log chanel.
extern WTFLogChannel WebKitLogTextInput;
extern WTFLogChannel WebKitLogTiming;等全局变量.
这样我们只要引用了WebKitLogging.h便能使用类似,LOG(myChannel,“mytest”)的打印语句。进而观察WebKitLogging.cpp详细的定义了各个全局变量,如下:
WTFLogChannel WebKitLogTextInput = { 0x00000010, "WebKitLogLevel", WTFLogChannelOff };
WTFLogChannel WebKitLogTiming = { 0x00000020, "WebKitLogLevel", WTFLogChannelOff };
这里的结构体初始化的最后一个参数就是控制log是否打印的开关。如:WTFLogChannelOff 就是控制。
同理 Logging.h也定义了类似的LOG_CHANNEL_PREFIX 以及全局变量,而我们的webCore里面的打印基本都使用的Logging.h。
使用该Log输出方式打印出一个简单的render树如下:
begin to visitor RenderTree
visitor index=0-#document
visitor index=1-HTML
visitor index=2-BODY
visitor index=3-#text
visitor index=3-#text
visitor index=3-BUTTON
visitor index=4 / /匿名标签生成
visitor index=5-#text
visitor index=3-#text
visitor index=3-#text
综上所述总结一下webkit对于log打印的设计思想:
1.WebKit的wtf里面封装了log打印的逻辑,并且以LOG宏的形式提供给外部使用,而真实实现函数WTFLog不暴露而被隐藏掉了。调用则使用宏非常简洁,代码得以优雅。
2.调用者只需要引用WebKitLogging.h或Logging.h,从而回避了去定义channel变量这个过程,也是就说webkit意在隐藏掉WTFLogChannel这个数据结构的存在,而又要能控制log输出开关。
3. 使用channel全局变量进行配置,并在对应.cpp文件中进行修改,使得如果即使修改控制开关,也不会引起全编译。
4.使用Channel对log输出进行管理,还能控制打印级别,后面可以在WTFLog函数中添加控制逻辑,也不会影响其他代码。