文章转载自:liangkz
我们仍然是先对子系统的目录结构做一次整理,做成表格,把模块之间的调用关系理一下:
1. Hi3861 平台如上表,上电后在SystemInit阶段会依次init:
HiviewConfigInit、HiLogInit、hiview service:
[system_init] HOS_SystemInit begin: %%%%%%%%%%%
[system_init] MODULE_INIT(core)============================
[hiview_config] CORE_INIT_PRI(HiviewConfigInit, 0);
[hiview_log] HiLogInit. CORE_INIT_PRI(HiLogInit, 0)
[hiview_output_log] InitCoreLogOutput. call HiviewRegisterMsgHandle
[system_init] SYS_INIT(service)============================
[hiview_service] SYS_SERVICE_INIT(Init).
[samgr_lite] RegisterService(name: hiview)
[hiview_service] Init.InitHiviewComponent.
[hiview_file] InitHiviewFile
......
......
[hiview_service] Initialize.
我们分别来看一下他们都做了些什么事情:
A. HiviewConfigInit
位于:Hi3861/base/hiviewdfx/utils/lite/hiview_config.c
其主要是初始化全局变量g_hiviewConfig的配置,如下
- hiviewInited:标记hiview service是否已经完成初始化。hiview service完成初始化之后将其置为TRUE,“The communication of task can be use after the service is running.”,包括了相关的Message Handle才可用。
- outputOption:标记Control log output mode. default to OUTPUT_OPTION_FLOW.
- typedef enum {
- //不经过buffer,直接在终端上实时打印log,Debug版本建议用这个
- OUTPUT_OPTION_DEBUG = 0, /* Output to the UART without buffer. Commercial versions are forbidden. */
- //不会实时打印log,先保存到buffer里,满足条件时才会通过SAMGR发消息一次性打印一条或多条log到终端上
- OUTPUT_OPTION_FLOW, /* Output to UART via SAMGR */
- //不会实时打印log,先保存到buffer里,满足条件时才会通过SAMGR发消息将buffer的log写入TextFile
- OUTPUT_OPTION_TEXT_FILE, /* Output to Text File: “/user/log/debug.log”, see ‘HIVIEW_FILE_PATH_LOG’ */
- //不会实时打印log,先保存到buffer里,满足条件时才会通过SAMGR发消息将buffer的log写入BinFile
- OUTPUT_OPTION_BIN_FILE,
- OUTPUT_OPTION_MAX
- } HiviewOutputOption;
复制代码- level:标记Control log output level. Default to HILOG_LV_DEBUG.
低于此标记级别的log不会打印出来(?),还是不会被记录到TEXT_FILE/BIN_FILE里(??)
- #define HILOG_LV_INVALID 0
- #define HILOG_LV_DEBUG 1
- #define HILOG_LV_INFO 2
- #define HILOG_LV_WARN 3
- #define HILOG_LV_ERROR 4
- #define HILOG_LV_FATAL 5
- #define HILOG_LV_MAX 6
复制代码 B. HiLogInit
位于:Hi3861/base/hiviewdfx/frameworks/hilog_lite/mini/hiview_log.c
先去初始化全局变量HiviewCache
g_logCache
- typedef struct {
- HiviewMutexId_t mutex;
- uint16 wCursor; // 0-65535
- uint16 usedSize; // 0-65535,buffer已经使用掉的size
- uint16 size; // cache size 0-65535,由LOG_STATIC_CACHE_SIZE指定,1024 Byte
- HiviewCacheType type;
- uint8 *buffer; // Circular buffer,由g_logCacheBuffer 指定的空间,1024 Byte
- } HiviewCache;
复制代码而全局变量 HiviewFile
g_logFile,这是在上面将outputOption标记设置为 OUTPUT_OPTION_TEXT_FILE/BIN_FILE 时用得上的配置,如输出TextFile时,文件会生成在“user/log/debug.log”。
接下来是注册三个MsgHandle,用于处理满足条件时,将g_logCache/g_logCacheBuffer 中的log写入文件 或者通过Uart在默认终端上打印出来。这中间还有一个重新格式化log的处理,如:HILOG_INFO(HILOG_MODULE_SAMGR, "Initialized all system and application services!");
这条log会处理成:00 00:00:00 0 220 I 1/SAMGR: Initialized all system and application services!
前面新增一串字符分别代表着:开机以来的天数+时:分:秒+0+task+logLevel+module+moduleName.
task这个看起来并不是打印这个log的任务的TaskID,貌似是可以自行修改的,待验证。
logLevel 就是:
- static char g_logLevelInfo[HILOG_LV_MAX] = {
- 'N', // "NONE"
- 'D', // "DEBUG"
- 'I', // "INFO"
- 'W', // "WARN"
- 'E', // "ERROR"
- 'F' // "FATAL"
- };
复制代码module就是枚举HiLogModuleType定义的数字,这里的1就是 HILOG_MODULE_SAMGR 的值。
moduleName就是module对应的字符名字,见下面通过HiLogRegisterModule()注册的module参数。
注册log的模块,按照官方文档的“Hilog_lite开发指导”中的例子进行配置即可。
注册InitLogOutput()是为了在hiview service init时创建和初始化log输出的TextFile/BinFile等文件的相关信息,outputOption ==OUTPUT_OPTION_DEBUG/OUTPUT_OPTION_FLOW 是不需要创建文件的。
注册InitLogLimit()是为了在hiview service init时给log的打印设置一些限制条件:
SetLimitThreshold(HILOG_MODULE_HIVIEW, LOG_LIMIT_LEVEL3);
SetLimitThreshold(HILOG_MODULE_APP, LOG_LIMIT_LEVEL2);
过于频繁地打印log,有可能会导致log的丢失。
C. hiview service
位于:Hi3861/base/hiviewdfx/services/hiview_lite/hiview_service.c
先是向SAMGR注册了g_hiviewService以及FeatureApi,然后通过InitHiviewComponent()去依次执行 g_hiviewInitFuncList[ ] 中的 InitLogOutput()和InitLogLimit()做相关的配置。
注意,这里仅仅是向SAMGR注册服务和注册MsgHandle API而已,还需要等到后面SAMGR把各种资源环境配置好后,才会调用g_hiviewService.Initialize()去初始化和拉起hiview service,提供log方面的相关服务。
2. 在接下来的系统启动过程、系统运行过程、应用运行过程中,只要有调用上表C的声明中 log.h/hiview_log.h头文件定义的宏,来打印log,就都会跑到下面所分析的流程中去。
hiview_log.h 定义了:一些宏、枚举、辅助函数和
- /*
- * Interface for printing basic logs. Use the macro definition interface instead of directly using this interface.
- * [url=home.php?mod=space&uid=3142012]@param[/url] module Module ID.
- * @param level Log Level.
- * @param nums Parameters automatically generated by macro.
- * @param fmt Format string.
- * [url=home.php?mod=space&uid=988716]@attention[/url] Do not use this interface directly, you should use the HILOG_XXX interface.
- */
- void HiLogPrintf(uint8 module, uint8 level, const char *nums, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
- #define HILOG_DEBUG(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_DEBUG, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
- #define HILOG_INFO(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_INFO, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
- #define HILOG_WARN(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_WARN, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
- #define HILOG_ERROR(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_ERROR, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
- #define HILOG_FATAL(mod, fmt, ...) HiLogPrintf(mod, HILOG_LV_FATAL, FUN_ARG_NUM(__VA_ARGS__), fmt, ##__VA_ARGS__)
复制代码系统建议不要直接使用HiLogPrintf()来打印log,而是使用下面的一组宏来分级别/类型来打印log。
HiLogPrintf() 的实现在:Hi3861/base/hiviewdfx/frameworks/hilog_lite/mini/hiview_log.c
经过一些判断后,调用 Hi3861/base/hiviewdfx/frameworks/hilog_lite/mini/hiview_output_log.c
文件内的OutputLog():
Hi3861/base/hiviewdfx/services/hiview_lite/hiview_service.c
【这里的MessageHandle()和Output()函数,如果进一步深入去分析和理解,就会涉及到samgr_lite组件的一些东西了,这里先不进一步细说。文末我把我做的两张图附上,大家可以先自行理解一下。】
hiview service通过MessageHandle()调用之前InitCoreLogOutput()注册的MsgHandle API来处理相关消息:
实时打印log、把log写入TextFile或者写入BinFile。
最上面表格中“D的实现”中关于hiview_cache.c、hiview_file.c、hiview_util.c等辅助函数的实现以及DFX系统外部提供的支持,就请各位自己去研究了。
Hi3516工程上的目录结构,相比Hi3861的多了:
- hilog.cpp C++类对上面几个宏的封装;上层应用开发,有自己的JS/Java的相关类的封装,请自行根据API参考文档进行配置和调用。
- hiview_applogcat.c apphilogcat 服务进程,负责从日志设备(dev/hilog)读取数据,格式化输出到终端,同时也写入磁盘文件。
- hiview_logcat.c 编译成可执行程序hilogcat,看起来是可以通过shell执行,传入参数来动态调整log的打印,不过并不会写入磁盘文件。
我没有Hi3516平台,暂无法验证。
附上两张我在整理 HiviewService 时做的展开图片: