From beef36be0202b9eaa41423e34840d646f28c9b2d Mon Sep 17 00:00:00 2001 From: jichuan Date: Tue, 18 Jun 2024 17:20:42 +0800 Subject: [PATCH 1/6] =?UTF-8?q?0618=20trace=5Fstreamer=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jichuan --- trace_streamer/.gn | 0 trace_streamer/BUILD.gn | 2 +- trace_streamer/README.md | 118 ++++- trace_streamer/build/protoc_w.py | 2 +- trace_streamer/doc/des_tables.md | 392 ++++++++++++---- trace_streamer/figures/db_hisys_event.png | Bin 49219 -> 32425 bytes trace_streamer/figures/db_native_memory.png | Bin 125557 -> 38333 bytes trace_streamer/figures/dump_and_mem.png | Bin 8417 -> 13102 bytes trace_streamer/pare_third_party.sh | 7 +- .../prebuilts/patch_hiperf/BUILD.gn | 121 +++-- .../prebuilts/patch_hiperf/hiviewdfx_BUILD.gn | 71 ++- .../prebuilts/patch_hiperf/unique_fd.h | 185 -------- .../patch_libunwind/libunwindbuild.gn | 421 +++++++++--------- trace_streamer/sdk/demo_sdk/BUILD.gn | 6 - trace_streamer/sdk/demo_sdk/test/BUILD.gn | 2 +- .../demo_sdk/test/unittest/sdk_api_test.cpp | 202 ++++----- trace_streamer/sdk/demo_sdk/ts.gni | 3 +- trace_streamer/src/BUILD.gn | 5 +- trace_streamer/src/base/file.cpp | 18 + trace_streamer/src/base/file.h | 8 +- .../src/cfg/trace_streamer_config.cpp | 10 + .../src/cfg/trace_streamer_config.h | 10 + trace_streamer/src/filter/binder_filter.cpp | 53 ++- trace_streamer/src/filter/binder_filter.h | 5 +- .../src/filter/hook_filter/BUILD.gn | 5 +- .../filter/hook_filter/native_hook_filter.cpp | 3 +- .../filter/hook_filter/native_hook_filter.h | 2 +- .../offline_symbolization_filter.cpp | 35 -- .../offline_symbolization_filter.h | 1 - trace_streamer/src/filter/slice_filter.cpp | 33 +- trace_streamer/src/filter/slice_filter.h | 6 +- trace_streamer/src/main.cpp | 18 +- trace_streamer/src/parser/BUILD.gn | 14 - .../src/parser/ebpf_parser/BUILD.gn | 13 +- .../ebpf_parser/bio_latency_data_parser.cpp | 5 +- .../src/parser/ebpf_parser/ebpf_base.cpp | 3 +- .../src/parser/ebpf_parser/ebpf_base.h | 2 +- .../ebpf_parser/file_system_data_parser.cpp | 5 +- .../ebpf_parser/paged_memory_data_parser.cpp | 5 +- .../src/parser/hiperf_parser/BUILD.gn | 26 +- .../parser/hiperf_parser/perf_data_parser.cpp | 100 ++++- .../parser/hiperf_parser/perf_data_parser.h | 8 +- .../src/parser/pbreader_parser/BUILD.gn | 16 +- .../htrace_parser/htrace_event_parser.cpp | 79 +++- .../htrace_parser/htrace_event_parser.h | 8 + .../native_hook_parser/BUILD.gn | 2 +- .../pbreader_native_hook_parser.h | 4 +- .../pbreader_parser/pbreader_parser.cpp | 160 ++++++- .../parser/pbreader_parser/pbreader_parser.h | 22 + .../src/parser/print_event_parser.cpp | 67 ++- .../src/parser/print_event_parser.h | 8 +- .../bytrace_parser/bytrace_event_parser.cpp | 35 +- .../bytrace_parser/bytrace_event_parser.h | 2 + .../src/parser/rawtrace_parser/BUILD.gn | 8 +- .../rawtrace_parser/cpu_detail_parser.cpp | 64 +++ .../rawtrace_parser/cpu_detail_parser.h | 8 + .../ftrace_event_processor.cpp | 68 +++ .../rawtrace_parser/ftrace_event_processor.h | 4 + trace_streamer/src/rpc/ffrt_converter.cpp | 83 ++-- trace_streamer/src/rpc/wasm_func.cpp | 7 - trace_streamer/src/rpc/wasm_func.h | 1 - trace_streamer/src/table/ftrace/BUILD.gn | 1 + .../src/table/ftrace/dma_fence_table.cpp | 162 +++++++ .../table/ftrace/include/dma_fence_table.h | 53 +++ trace_streamer/src/table/hiperf/BUILD.gn | 1 + .../hiperf/include/perf_napi_async_table.h} | 53 ++- .../table/hiperf/perf_napi_async_table.cpp | 200 +++++++++ .../src/trace_data/trace_data_cache.cpp | 4 + .../src/trace_data/trace_data_cache_base.h | 2 + .../trace_data/trace_data_cache_reader.cpp | 8 + .../src/trace_data/trace_data_cache_reader.h | 2 + .../trace_data/trace_data_cache_writer.cpp | 9 + .../src/trace_data/trace_data_cache_writer.h | 2 + .../trace_data/trace_stdtype/base_stdtype.h | 2 +- .../ftrace/render_service_stdtype.cpp | 41 ++ .../ftrace/render_service_stdtype.h | 38 ++ .../trace_stdtype/hiperf/hiperf_stdtype.cpp | 91 ++++ .../trace_stdtype/hiperf/hiperf_stdtype.h | 39 ++ trace_streamer/test/BUILD.gn | 66 ++- trace_streamer/test/resource/Mmap.htrace | Bin trace_streamer/test/resource/ebpf_bio.htrace | Bin .../resource/hiprofiler_data_ability.htrace | Bin .../test/resource/hiprofiler_data_perf.htrace | Bin .../htrace_perf_no_profiler_header.bin | Bin .../test/resource/perfCompressed.data | Bin 85 files changed, 2310 insertions(+), 1035 deletions(-) mode change 100755 => 100644 trace_streamer/.gn delete mode 100644 trace_streamer/prebuilts/patch_hiperf/unique_fd.h create mode 100644 trace_streamer/src/table/ftrace/dma_fence_table.cpp create mode 100644 trace_streamer/src/table/ftrace/include/dma_fence_table.h rename trace_streamer/{prebuilts/patch_hiperf/file_ex.h => src/table/hiperf/include/perf_napi_async_table.h} (33%) create mode 100644 trace_streamer/src/table/hiperf/perf_napi_async_table.cpp mode change 100755 => 100644 trace_streamer/test/resource/Mmap.htrace mode change 100755 => 100644 trace_streamer/test/resource/ebpf_bio.htrace mode change 100755 => 100644 trace_streamer/test/resource/hiprofiler_data_ability.htrace mode change 100755 => 100644 trace_streamer/test/resource/hiprofiler_data_perf.htrace mode change 100755 => 100644 trace_streamer/test/resource/htrace_perf_no_profiler_header.bin mode change 100755 => 100644 trace_streamer/test/resource/perfCompressed.data diff --git a/trace_streamer/.gn b/trace_streamer/.gn old mode 100755 new mode 100644 diff --git a/trace_streamer/BUILD.gn b/trace_streamer/BUILD.gn index 6babb6dbd..22c6002d7 100644 --- a/trace_streamer/BUILD.gn +++ b/trace_streamer/BUILD.gn @@ -27,7 +27,7 @@ group("trace_streamer") { } else if (is_sdkdemo) { deps = [ "sdk/demo_sdk:trace_streamer_sdk_builtin" ] } else if (is_sdkdemo_test) { - deps = [ "sdk/test:sdkunittest" ] + deps = [ "sdk/demo_sdk/test:sdkunittest" ] } else if (use_wasm) { deps = [ "src:trace_streamer_builtin" ] } else { diff --git a/trace_streamer/README.md b/trace_streamer/README.md index 9ca966509..a94ab67ff 100644 --- a/trace_streamer/README.md +++ b/trace_streamer/README.md @@ -34,16 +34,83 @@ TraceStreamer可以WebAssembly方式在浏览器中运行,相关接口在wasm extern "C" { /* 初始化wasm,在JS中注册回调函数,并返回一段可复用的内存空间,由JS调用 * - * @ replyFunction: 回调函数 -* @ reqBufferSize: 返回的内存长度 -* return: 返回一段内存地址给JS + * @ replyFunction: 回调函数,返回json格式的数据。 + * @ reqBufferSize: js在wasm中申请的内存大小。 + * @ replyTLVFunction: 回调函数,返回proto格式的sql查询结果。 + * @ ffrtConvertedReply: 回调函数,返回ffrt转换后生成的新trace文件。 + * return: 返回一段内存地址给JS */ -EMSCRIPTEN_KEEPALIVE uint8_t* Initialize(ReplyFunction replyFunction, uint32_t reqBufferSize) +EMSCRIPTEN_KEEPALIVE uint8_t *Initialize(uint32_t reqBufferSize, + ReplyFunction replyFunction, + TLVReplyFunction replyTLVFunction, + ReplyFunction ffrtConvertedReply) + +extern "C" { +/* 初始化wasm,在JS中注册大文件切割或按时间切割文件的回调函数,并返回一段可复用的内存空间,由JS调用 + * + * @ splitFileFunction: 回调函数, 返回大文件切割,或者按时间切割后生成的数据。 + * @ reqBufferSize: js在wasm中申请的内存大小。 + * return: 返回一段内存地址给JS +*/ +EMSCRIPTEN_KEEPALIVE uint8_t *InitializeSplitFile(SplitFileFunction splitFileFunction, uint32_t reqBufferSize) + +/* 通知wasm,按时间切割文件的时间信息已发送,并且通知时间信息大小为dataLen字节 + * + * @ dataLen 切割文件的时间信息长度。 时间数据保存在InitializeSplitFile接口申请的内存中。数据格式:"startTS;endTS;"。 + * return: bool类型。解析切割时间信息成功返回true,否则返回false。 +*/ +EMSCRIPTEN_KEEPALIVE int TraceStreamerSplitFileEx(int dataLen) + +/* 通知wasm,按时间切割的文件信息已发送,并且通知发送的文件信息大小为dataLen字节 + * + * @ dataLen 已经发送的被切割的文件数据长度,数据内容保存在InitializeSplitFile接口申请的内存中。 + * @ isFinish bool类型, 被切割的文件数据发送完成为true, 否则为false。 + * return: 切割成功返回0,否则返回-1。 +*/ +EMSCRIPTEN_KEEPALIVE int TraceStreamerReciveFileEx(int32_t dataLen, int32_t isFinish) + +/* JS在wasm中申请一段内存,用于传输config配置信息。如: ffrt convert开关,animation开关,taskpool开关等 + * + * @ reqBufferSize JS准备申请的内存大小。 + * return: 返回一段内存地址给JS。 +*/ +EMSCRIPTEN_KEEPALIVE uint8_t *InitializeParseConfig(uint32_t reqBufferSize) + +/* JS通知wasm已经发送了config信息,并通知发送的数据大小。 wasm解析config信息。 + * + * @ dataLen JS发送config信息大小, 数据内容保存在InitializeParseConfig接口申请的内存中。 + * return: wasm解析config信息成功返回0,否则返回-1。 +*/ +EMSCRIPTEN_KEEPALIVE int TraceStreamerParserConfigEx(int dataLen) + +/* JS通知wasm已经发送大文件切割的时间信息,并通知发送的数据大小。 wasm解析大文件切割的时间信息。 + * + * @ dataLen JS发送大文件切割时间信息长度。时间信息格式为proto数据的1024头,保存在InitializeSplitFile接口申请的内存中。 + * return: wasm解析大文件切割时间信息成功返回0,否则返回-1。 +*/ +EMSCRIPTEN_KEEPALIVE int TraceStreamerGetLongTraceTimeSnapEx(int dataLen) + +/* JS通知wasm已经发送大文件切割的文件数据,并通知发送的数据大小。 + * + * @ dataLen JS发送大文件切割文件数据大小。 + * @ isFinish JS发送数据是否结束。 + * @ pageNum 准备按照第pageNum个文件的时间范围,切割大文件。 + * return: wasm解析大文件切割时间信息成功返回0,否则返回-1。 +*/ +EMSCRIPTEN_KEEPALIVE int TraceStreamerLongTraceSplitFileEx(int dataLen, int32_t isFinish, uint32_t pageNum) + +/* 导入so重新符号化业务,JS通知wasm申请内存保存导入so的文件名称,并设置导入so重新符号化的回调函数。 + * + * @ parseELFCallback 回调函数 + * @ reqBufferSize 申请的文件大小 + * return: 返回一段内存给JS。 +*/ +EMSCRIPTEN_KEEPALIVE uint8_t *InitFileName(ParseELFFunction parseELFCallback, uint32_t reqBufferSize) /* 更新起始结束时间,由JS调用 * * @ len: 起始和结束时间组成的字符串长度 -* return: 成功返回0。 + * return: 成功返回0。 */ EMSCRIPTEN_KEEPALIVE int UpdateTraceTime(int len) @@ -55,6 +122,12 @@ EMSCRIPTEN_KEEPALIVE int UpdateTraceTime(int len) */ EMSCRIPTEN_KEEPALIVE uint8_t* TraceStreamerSetThirdPartyDataDealer(SendDataCallBack sendDataCallBack, uint32_t reqBufferSize) +/* 设置TraceStreamer可以打印的日志级别,由JS调用 + * + * @ level: 允许打印日志的最低级别。 +*/ +EMSCRIPTEN_KEEPALIVE void TraceStreamerSetLogLevel(uint32_t level) + /* TraceStreamer的数据解析接口,由JS调用 * * @ dataLen: 需要解析的数据源长度 @@ -62,13 +135,26 @@ EMSCRIPTEN_KEEPALIVE uint8_t* TraceStreamerSetThirdPartyDataDealer(SendDataCallB */ EMSCRIPTEN_KEEPALIVE int TraceStreamerParseDataEx(int dataLen, bool isFinish) +/* 导入so重新符号化业务的下载so文件接口,由JS调用 + * + * @ totalLen: 传输的文件总长度。 + * @ fileNameLen: 传输的文件名长度。 + * @ dataLen: 当前调用传输的文件数据长度。 + * @ finish: 当前文件是否传输结束。 + * return: wasm下载并保存该文件成功返回0,失败返回-1 +*/ +EMSCRIPTEN_KEEPALIVE int32_t TraceStreamerDownloadELFEx(int32_t totalLen, + int32_t fileNameLen, + int32_t dataLen, + int32_t finish) + /* TraceStreamer停止解析数据,由JS调用 * * return: 成功返回0,失败返回-1 */ EMSCRIPTEN_KEEPALIVE int TraceStreamerParseDataOver() -/* 数据库操作接口,由JS调用 +/* 数据库操作接口,由JS调用,返回json格式的sql查询结果。 * * @ sqlLen: 需要执行的操作类sql语句长度 * return: 成功返回0,失败返回-1 @@ -94,6 +180,19 @@ EMSCRIPTEN_KEEPALIVE int TraceStreamerSqlQueryEx(int sqlLen) */ EMSCRIPTEN_KEEPALIVE int TraceStreamerCancel() +/*执行查询类sql语句,由JS调用。返回proto格式的sql查询结果。 + * +* @ sqlLen: 需要执行的查询类sql语句长度 +* return: 成功返回0,失败返回-1 +*/ +EMSCRIPTEN_KEEPALIVE int32_t TraceStreamerSqlQueryToProtoCallback(int32_t sqlLen) +/*执行metrics数据查询 + * +* @ sqlLen: 需要执行的查询类sql语句长度 +* return: 成功返回0,失败返回-1 +*/ +EMSCRIPTEN_KEEPALIVE int32_t TraceStreamerSqlMetricsQuery(int32_t sqlLen) + /*发送数据给第三方wasm解析,由TraceStreamer调用 * * @ pluginData: 第三方插件的数据源 @@ -103,6 +202,13 @@ EMSCRIPTEN_KEEPALIVE int TraceStreamerCancel() */ int TraceStreamerPluginOutSendData(const char* pluginData, int len, const std::string componentName) +/*返回解析数据库 + * + * @ fun: 回调函数,负责返回解析生成的数据库。 + * return: 成功返回0 +*/ +EMSCRIPTEN_KEEPALIVE int32_t WasmExportDatabase(ExportDBCallback fun) + /* 初始化配置接口,由JS调用 * * @ dataLen: 配置字符串的长度 diff --git a/trace_streamer/build/protoc_w.py b/trace_streamer/build/protoc_w.py index de4cfdcdb..3dd7b13a4 100755 --- a/trace_streamer/build/protoc_w.py +++ b/trace_streamer/build/protoc_w.py @@ -49,7 +49,7 @@ PARAMS_ALL = f"{PARAMS_SRC}" if not sys.argv[4].startswith("--plugin"): if os.path.isfile(OPT_PLUGIN_PROTOREADER_PATH): - cmd = [PROTOC, OPT_PLUGIN_PROTOREADER, f"{PLUGINOUT}:{sys.argv[5]}", *PARAMS_ALL.split()] + cmd=[PROTOC, OPT_PLUGIN_PROTOREADER, f"{PLUGINOUT}:{sys.argv[5]}", *PARAMS_ALL.split()] print("执行参数:--------------- ", cmd, " --------------------------") subprocess.run(cmd) subprocess.run([PROTOC, *PARAMS_ALL.split()]) diff --git a/trace_streamer/doc/des_tables.md b/trace_streamer/doc/des_tables.md index 1dc29b4eb..fa1e90f30 100755 --- a/trace_streamer/doc/des_tables.md +++ b/trace_streamer/doc/des_tables.md @@ -37,6 +37,7 @@ TraceStreamer可以将trace数据源转化为易于理解和使用的数据库 | frame_slice | 记录RS(RenderService)和应用的帧渲染| | gpu_slice | 记录RS的帧对应的gpu渲染时长| | hidump | 记录FPS(Frame Per Second)数据| +| hisys_all_event | 记录了所有HiSysEvent事件相关的原始数据 | | hisys_event_measure | 记录了HiSysEvent事件相关数据,目前HiSysEvent事件包括了异常事件,IDE事件,器件状态事件 | | instant | 记录Sched_waking, sched_wakeup事件, 用作ThreadState表的上下文使用 | | irq | 记录中断相关事件| @@ -68,7 +69,7 @@ TraceStreamer可以将trace数据源转化为易于理解和使用的数据库 | paged_memory_sample | 记录内存操作相关方法调用,及调用栈数据| | perf_callchain | 记录Hiperf采样数据的调用栈信息| | perf_files | 记录Hiperf工具采集到的函数符号表和文件名| -| perf_report | 记录Hiperf工具采集数据时的配置信息。包括|抓取的事件类型,抓取数据的命令, 抓数据时指定的进程名称| +| perf_report | 记录Hiperf工具采集数据时的配置信息。包括抓取的事件类型,抓取数据的命令, 抓数据时指定的进程名称| | perf_sample | 记录Hiperf工具的采样信息| | perf_thread | 记录Hiperf工具采集到的进程和线程数据| | process | 记录所有的进程信息| @@ -79,6 +80,9 @@ TraceStreamer可以将trace数据源转化为易于理解和使用的数据库 | smaps | 记录进程的内存消耗的相关信息采样| | stat | 此结果用来统计数据解析中各类数据的数据条数,数据和合法性,数据的匹配程度(begin-end),数据的损失等,查看此结构对应的表,可对数据源有基本的了解| | static_initalize | 记录了so初始化相关数据| +| memory_cpu | 记录了cpu内存数据 | +| memory_profile | 记录了sys/kernel/debug/mali0/ctx/$(pidof xxx)/mem_profile节点相关数据| +| memory_rs_image | 记录了hidumper抓取的界面的内存大小相关数据| | symbols | 记录系统调用名称和其函数指针的对应关系,trace中用addr来映射function_name来节省存储空间| | syscall | 记录用户空间函数与内核空间函数相互调用记录| | sys_event_filter | 记录所有的filter| @@ -110,6 +114,7 @@ TraceStreamer可以将trace数据源转化为易于理解和使用的数据库 |frame_slice | - |ftrace-plugin |帧渲染数据 | |gpu_slice | - |ftrace-plugin |gpu渲染时长 | |hidump | - |hidump-plugin |FPS数据 | +|hisys_all_event | - |hisysevent-plugin |JSON数据源 | |hisys_event_measure | - |hisysevent-plugin |JSON数据源 | |instant | - |ftrace-plugin |waking和wakeup事件 | |irq | - |ftrace-plugin |记录中断事件 | @@ -127,6 +132,9 @@ TraceStreamer可以将trace数据源转化为易于理解和使用的数据库 |js_heap_trace_node | - |arkts-plugin | js内存数据 | |app_startup | - |ftrace-plugin | 应用启动数据 | |static_initalize | - |ftrace-plugin | so初始化数据 | +|memory_cpu | - |hidumper-plugin | cpu内存数据 | +|memory_profile | - |hidumper-plugin |/sys/kernel/debug/mali0/ctx/$(pidof xxx)/mem_profile节点相关数据| +| memory_rs_image | - |hidumper-plugin |hidumper抓取的界面内存大小数据| |live_process | - |process-plugin |Monitor数据 | |network | - |network-plugin |Monitor数据 | |diskio | - |diskio-plugin |Monitor数据 | @@ -271,7 +279,6 @@ js_heap_sample:记录timeline的时间轴信息 | Columns Name | SQL TYPE | |---- |---- | |id |INT | -|flag |INT | |app_name |INT | |app_key |INT | #### 表描述 @@ -293,6 +300,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录方法的参数集合。 #### 字段详细描述 +- id: 唯一标识 - key:键 - datatype:数据类型 - value:取值 @@ -319,6 +327,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录IO操作相关方法调用,及调用栈数据。 #### 字段详细描述 +- id: 唯一标识 - callchain_id:调用栈的唯一标识。与ebpf_callstack表中Callchain_id字段关联 - type:事件类型其取值为枚举类型(DATA_READ,DATA_WRITE,METADATA_READ,- METADATA_WRITE,PAGE_IN,PAGE_OUT) - ipid:TS内部进程号 @@ -350,18 +359,23 @@ js_heap_sample:记录timeline的时间轴信息 |spanId |TEXT | |parentSpanId |TEXT | |flag |TEXT | -|args |TEXT | #### 表描述 记录调用堆栈和异步调用信息,其中depth,stack_id和parent_stack_id仅在非异步的调用中有效。当cookid不为空时,为异步调用,此时callid为进程唯一号,否则为线程唯一号。 #### 字段详细描述 +- id: 唯一标识 +- ts: 数据事件上报时间戳 - dur:调用时长 - callid:调用者的ID,比如针对线程表里面的id +- cat: 表示当前栈帧属于哪个业务(binder/workqueue/null) - name:调用名称 -- depth:调用深度 +- depth:调用深度 +- cookie: 异步调用的cookie值 - parent_id:父调用的id -- spanId:分布式调用关联关系 +- argsetid: 调用的参数列表,关联args表的id字段 +- chainId:分布式数据中的chainId,id相同则表示为同一个分布式的调用栈 +- spanId:分布式调用关联关系,当前帧的id +- parentSpanId: 分布式调用关联关系,当前帧的parent的SpanId,对应当前表的spandId - flag:C表示分布式调用发送方,S表示接受方 -- args:分布式调用函数参数 ### clk_event_filter表 #### 表结构 @@ -374,8 +388,10 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录时钟信息。 #### 字段详细描述 -- Type:时钟事件类型 -- Name:时钟事件名称 +- id: 与measure表的filterId字段关联 +- type:时钟事件类型 +- name:时钟事件名称 +- cpu: cpu编号 ### clock_event_filter表 #### 表结构 @@ -384,25 +400,28 @@ js_heap_sample:记录timeline的时间轴信息 |id |INT | |type |TEXT | |name |TEXT | -|cpu |INT | +|cpu |INT | #### 表描述 此结构用来维护时钟事件,cpu与唯一的ID做关联。 #### 主要字段描述 +- id: 与measure表的filterId字段关联 - Type:时钟事件类型 - Name:时钟事件名称 +- cpu: cpu编号 ### cpu_measure_filter表 #### 表结构 | Columns Name | SQL TYPE | |---- |---- | |id |INT | -|type |TEXT | |name |TEXT | |cpu |INT | #### 表描述 将cpu号作为key1,cpu的频率,空闲等状态作为key2,唯一确定一个filter_id。 #### 主要字段描述 -- Id(filterid), cpu:事件名称,cpu号 +- id: 与measure表的filterId字段关联 +- name: 事件名(cpu_idle/cpu_frequency/cpu_frequency_limits_max/cpu_frequency_limits_min) +- cpu:cpu号 ### cpu_usage表 #### 表结构 @@ -415,8 +434,10 @@ js_heap_sample:记录timeline的时间轴信息 |system_load |REAL | |process_num |INT | #### 表描述 -记录了与CPU使用率相关的数据。 +记录了/proc/pid/stat与CPU使用率相关的数据。 #### 主要字段描述 +- ts: 数据上报时间 +- dur: 持续时间 - total_load:总负荷 - user_load:用户负载 - system_load:系统负载 @@ -444,6 +465,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 此表记录了一个数据类型ID和数据描述的映射。 #### 主要字段描述 +- id: 唯一标识 - typeId::数据类型id - Desc:数据类型描述 @@ -464,9 +486,16 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录了与磁盘读写相关的数据。 #### 主要字段描述 -- rd_sectors_kb:读数据的速度 -- wr_sectors_kb:写入数据的速度 - ts:时间戳 +- dur: 持续时间 +- rd: 当前时间段的读取量 +- wr: 当前时间段的写入量 +- rd_speed:当前时间段的读取速度 +- wr_speed:当前时间段的写入速度 +- rd_count:读取的数据总量 +- wr_count:写入的数据总量 +- rd_sectors_speed:读数据的平均速度 +- wr_sectors_speed:写入数据的平均速度 ### ebpf_callstack表 #### 表结构 @@ -481,6 +510,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录了与磁盘读写相关的数据。 #### 主要字段描述 +- id: 唯一标识 - callchain_id:调用栈的唯一标识 - depth:调用栈深度。取值为零时表示栈顶 - ip:调用栈ip @@ -512,7 +542,8 @@ js_heap_sample:记录timeline的时间轴信息 #### 主要字段描述 - callchain_id:调用栈信息ID与file_system_callstack表中call_chain_id字段相关联 - type:对应文件操作open,close,read,write -- ipid:线程所属的进程ID +- ipid:样本所属的内部进程ID,关联process表id +- itid: 样本所属的内部线程ID,关联thread表id - start_ts:开始时间 - end_ts:结束时间 - dur:耗时 @@ -536,6 +567,8 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 此表记录了设备的帧率信息,fps。 #### 相关字段描述 +- id: 唯一标识 +- ts: 数据上报时间戳 - fps:帧率值 ### hisys_event_measure表 @@ -552,7 +585,8 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录所有的system event事件的相关数据,及其相关表的映射信息。 #### 相关字段描述 -- serial:每条数据过来携带唯一一条id作为标识 +- serial:每条数据过来携带唯一一条id作为标识 +- ts: 数据上报时间戳 - name_id:存放事件对应的ID,与data_dict表相关联可以取出对应的字段 - key_id:存放事件包含的字段的ID,与表app_name的id字段相关联,找到app_name表的 id字段对应行的app_key字段与表data_dict表相关联取出对应的字段 - type:存放事件所包含的字段的值所属的类型为int型还是string(0为int,1为string) @@ -593,21 +627,21 @@ js_heap_sample:记录timeline的时间轴信息 |cookie |INT | |parent_id |INT | |argsetid |INT | -|chainId |TEXT | -|spanId |TEXT | -|parentSpanId |TEXT | |flag |TEXT | -|args |TEXT | #### 表描述 记录中断相关事件。 #### 相关字段描述 +- id: 唯一标识 +- ts: 数据上报时间戳 - dur:调用中断时长 - callid:调用中断者的ID,比如针对线程表里面的id -- cat:调用栈数据类型(取值范围:irq,softirq...) +- cat:调用栈数据类型(取值范围:irq,softirq, ipi) - name:调用中断的名称 - depth:中断调用的深度 +- cookie: 异步调用的cookie值 - parent_id:父调用中断的id -- spanId:分布式调用中断关联关系 +- argsetid: 跟arg_view中的argset关联,保存irq的名字以及值(irq=5 name=IPI) +- flag: 1表示硬中断 ### js_config表 @@ -706,7 +740,7 @@ js_heap_sample:记录timeline的时间轴信息 | file_name | TEXT | | start_time | INT | | end_time | INT | -| pid | INT | +| self_size | INT | #### 表描述 记录了js内存数据的文件名称和时间。 #### 相关字段描述 @@ -714,7 +748,7 @@ js_heap_sample:记录timeline的时间轴信息 - file_name:文件名称 - start_time:数据抓取的起始时间 - end_time:数据抓取的终止时间 -- pid:进程号 +- self_size: 当前snapshot中所有node的size之和 ### js_heap_info表 #### 表结构 @@ -912,8 +946,11 @@ js_heap_sample:记录timeline的时间轴信息 |disk_writes |INT | |disk_reads |INT | #### 表描述 -记录了一些实时的进程中执行的一些数据(Monitor)。 +记录了一些实时的进程中(/proc/$PID/status、/proc/$PID/stat、/proc/stat)执行的一些数据(Monitor)。 #### 主要字段描述 +- ts: 数据上报时间戳 +- dur: 事件持续时间 +- cpu_time: /proc/$PID/stat 中的cpu时间 - process_id:进程id - process_name:进程名 - parent_process_id:父进程的id @@ -940,13 +977,14 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录日志信息。 #### 关键字段描述 -- Seq:日志序号,保证日志解析的准确性 -- Ts:打印日志时间 -- Pid:日志的进程号 -- Tid:日志的线程号 -- Level:日志级别 -- Tag:日志标签 -- Context:日志内容 +- seq:日志序号,保证日志解析的准确性 +- ts:打印日志时间 +- pid:日志的进程号 +- tid:日志的线程号 +- level:日志级别 +- tag:日志标签 +- context:日志内容 +- origints:log中自带的时间 ### measure表 #### 表结构 @@ -978,7 +1016,10 @@ js_heap_sample:记录timeline的时间轴信息 记录一个递增的filterid队列,所有其他的filter类型在获取过程中,均从此数据列表中获取下一个可用的filter_id并做记录。 #### 字段详细描述 过滤分类(type),过滤名称(key2),数据ID(key1)。 -数据ID在process_measure_filter, sys_event_filter中作为id。 +id: 唯一的filterId,与process_measure_filter, sys_event_filter中的id关联。 +type:各种类型(cpu_measure_filter,clk_rate_filter,process_measure_filter...) +name: type的子类型。 +source_arg_set_id: 同一个source_arg_set_id代表一组数据,一般取得是itid或者cpu编号。 ### meta表 #### 表结构 @@ -1013,17 +1054,21 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录native_hook抓取的某个进程的堆内存,内存映射相关数据。 #### 关键字段描述 +- id: 唯一标识 - callChainId:唯一标识一条native_hook数据 +- ipid:所属的进程内部id, 关联process表中的id +- itid:所属的线程内部id, 关联thread表中的id - event_type:事件类型取值范围(AllocEvent,FreeEvent,MmapEvent, MunmapEvent) - sub_type_id:子事件类型(只有sub_type字段为MmapEvent时,该字段才会有值) - start_ts:申请内存开始时间 - end_ts:释放内存时间 -- Dur:申请内存活跃时间 -- Addr:申请内存地址 -- mem_size:申请或释放内存大小 -- all_mem_size:从采集数据开始到当前时刻,申请并活跃的内存总量。 event_type为AllocEvent或者FreeEvent时,表示活跃的堆内存总量。当event_type为MmapEvent或者MunmapEvent时,表示活跃的映射内存总量 +- dur:申请内存活跃时间 +- addr:申请内存地址 +- heap_size: 申请的内存大小 +- all_heap_size:从采集数据开始到当前时刻,申请并活跃的内存总量。 event_type为AllocEvent或者FreeEvent时,表示活跃的堆内存总量。当event_type为MmapEvent或者MunmapEvent时,表示活跃的映射内存总量 - current_size_dur:表示当前活跃内存总量的持续时间 -- last_lib_id:函数调用栈他最后一个函数所属的文件路径,除了文件名中带musl和libc++ +- last_lib_id:函数调用栈最后一个函数所属的文件路径,除了文件名中带musl和libc++ +- last_symbol_id: 函数调用栈最后一个函数名,lib除了文件名中带musl和libc++ ### native_hook_frame表 #### 表结构 @@ -1032,17 +1077,24 @@ js_heap_sample:记录timeline的时间轴信息 |id |INT | |callchain_id |INT | |depth |INT | +|ip |INT | |symbol_id |INT | |file_id |INT | |offset |INT | |symbol_offset |INT | +|vaddr |INT | #### 表描述 记录了内存的申请和释放的堆栈。 #### 相关字段描述 +- id: 唯一标识 - callchain_id:标识一组调用堆栈 - depth:调用栈深度 -- symbol_id:函数名 -- file_id:函数所属文件 +- ip: 函数ip +- symbol_id:函数名id,对应data_dict中id +- file_id:函数所属文件id,对应data_dict中id +- offset: 取自Frame message的offset字段 +- symbol_offset: 取自Frame message的symbol_offset字段 +- vaddr: 一般取值为offset + symbol_offset ### native_hook_statistic表 #### 表结构 @@ -1053,6 +1105,7 @@ js_heap_sample:记录timeline的时间轴信息 |ipid |INT | |ts |INT | |type |INT | +|sube_type_id |INT | |apply_count |INT | |release_count |INT | |apply_size |INT | @@ -1060,15 +1113,19 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 该表记录了内存申请/释放的统计信息。 -#### 关键字段描述 +#### 关键字段描述 +- id: 唯一标识 - callchain_id:内存分配的回调链id - ipid:进程id - ts:统计数据上报时间 - type:事件类型,0代表malloc事件,1代表mmap事件 +- sub_type_id:事件子类型,关联data_dict表id - apply_count:当前调用栈内存分配总次数 - release_count:当前调用栈内存释放总次数 - apply_size:当前调用栈累计分配总大小 - release_size:当前调用栈累计释放总大小 +- last_lib_id:函数调用栈最后一个函数所属的文件路径,除了文件名中带musl和libc++ +- last_symbol_id: 函数调用栈最后一个函数名,lib除了文件名中带musl和libc++ ### network表 #### 表结构 @@ -1088,10 +1145,17 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录了网络数据传输相关的信息。 #### 主要字段描述 -- tv_sec:时间,秒为单位 -- tv_nsec:时间,纳秒为单位 -- tx_bytes:网络数据的写入量 -- rx_bytes:网络数据的读取量 +- ts:事件上报时间 +- dur: 持续时间 +- tx: 网络数据的写入次数 +- rx: 网络数据的读取次数 +- tx_speed: 网络数据的写入次数/s +- rx_speed: 网络数据的读取次数/s +- packet_in:网络数据申请的数据包个数 +- packet_in_sec: 网络数据申请的数据包个数/s +- packet_out: 网络数据发送的数据包个数 +- packet_out_sec:网络数据发送的数据包个数/s +- net_type:网络类型,wifi/蜂窝 ### paged_memory_sample表 #### 表结构 @@ -1110,14 +1174,16 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录了网络数据传输相关的信息。 #### 主要字段描述 +- id: 唯一标识 - callchain_id: 取值相同的一组数据,表示一个完整的调用栈 - type:事件类型 - ipid:TS内部进程号 - start_ts:开始时间 - end_ts:结束时间 - dur:持续时间 -- size:操作页数 -- itid:TS内部线程号 +- size:操作页数,1页=4kb +- addr: 内存地址 +- itid:内部线程号 ### perf_callchain表 #### 表结构 @@ -1126,6 +1192,7 @@ js_heap_sample:记录timeline的时间轴信息 |id |INT | |callchain_id |INT | |depth |INT | +|ip |INT | |vaddr_in_file |INT | |file_id |INT | |symbol_id |INT | @@ -1133,8 +1200,10 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录了Hiperf采样数据的调用栈信息。 #### 主要字段描述 +- id: 唯一标识 - callchain_id:标识一组调用堆栈 - depth:调用栈深度 +- ip: 函数ip - vaddr_in_file:函数在文件中的虚拟地址 - file_id:与PerfFiles中的file_id字段相关联 - symbol_id:与PerfFiles中的symbol_id相关联 @@ -1152,6 +1221,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录Hiperf工具采集到的函数符号表和文件名。 #### 主要字段描述 +- id: 唯一标识 - file_id:文件编号 - serial_id:一个文件中可能有多个函数,serial_id表示函数的编号 - symbol:函数名 @@ -1167,6 +1237,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录Hiperf工具采集数据时的配置信息。包括:抓取的事件类型,抓取数据的命令, 抓数据时指定的进程名称。 #### 主要字段描述 +- id: 唯一标识 - report_type:数据类型。取值只有三种类型:config_name(事件类型), workload(抓取的进程名), cmdline(抓取命令) - report_value:对应类型的取值 @@ -1186,6 +1257,8 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录Hiperf工具的采样信息。 #### 主要字段描述 +- id: 唯一标识 +- callchain_id:关联perf_callchain表callchain_id - timestamp:未进行时钟源同步的时间戳 - thread_id:线程号 - event_count:采样统计 @@ -1205,6 +1278,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录Hiperf工具采集到的进程和线程数据。 #### 主要字段描述 +- id: 唯一标识 - thread_id:线程号 - process_id:进程号 - thread_name:线程名 @@ -1215,43 +1289,26 @@ js_heap_sample:记录timeline的时间轴信息 |---- |---- | |id |INT | |ipid |INT | -|type |TEXT | |pid |INT | |name |TEXT | |start_ts |INT | |switch_count |INT | |thread_count |INT | |slice_count |INT | -|mem_count |INT | +|mem_count |INT | #### 表描述 记录了进程相关数据。 #### 关键字段描述 - id:进程在数据库重新重新定义的id,从0开始序列增长 - ipid:TS内部进程id -- type:固定取值:process - pid:进程的真实id - name:进程名字 - start_ts:开始时间 - switch_count:统计内部有多少个线程有切换 - thread_count:统计其线程个数 -- slice_count:进程内有多个线程有slice数据 +- slice_count:进程内有多少个线程有slice数据 - mem_count:进程是否有内存数据 -### process_filter表 -#### 表结构 -| Columns Name | SQL TYPE | -|---- |---- | -|id |INT | -|type |TEXT | -|name |TEXT | -|ipid |INT | -#### 表描述 -将进程ID作为key1,进程的内存,界面刷新,屏幕亮度等信息作为key2,唯一确定一个filter_id, filter_id同时被记录在filter表中。 -#### 主要字段描述 -- id:进程id -- type:固定取值:process_filter -- name:进程名 -- ipid:该进程表中的id与process表中的id相关联 ### process_measure表 #### 表结构 @@ -1264,23 +1321,24 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 保存进程的内存,堆栈值等所有计量值信息。 #### 字段详细描述 +- type: 固定为measure - ts:事件时间 +- dur: 持续时间 - value:数值 -- filter_id:对应process_measure_filter表中的ID +- filter_id:对应process_measure_filter表中的id ### process_measure_filter表 #### 表结构 | Columns Name | SQL TYPE | |---- |---- | |id |INT | -|type |TEXT | |name |TEXT | |ipid |INT | #### 表描述 将进程ID作为key1,进程的内存,界面刷新,屏幕亮度等信息作为key2,唯一确定一个filter_id, filter_id同时被记录在measure_filter表中。 #### 字段详细描述 -- type:固定取值:process_measure_filter -- name:cpu状态名 +- id: 与measure表的filterId字段相关联 +- name:key名 - ipid:进程内部编号 ### raw表 @@ -1288,7 +1346,6 @@ js_heap_sample:记录timeline的时间轴信息 | Columns Name | SQL TYPE | |---- |---- | |id |INT | -|type |TEXT | |ts |INT | |name |TEXT | |cpu |INT | @@ -1296,34 +1353,39 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录了系统中的waking、wakup、cpu_idel、cpu_frequency数据。 #### 相关字段描述 -- type:固定字段(raw) +- id: 唯一标识 +- ts:事件时间 - name:调度名称(取值:cpu_idle,sched_wakeup,sched_waking) - cpu:事件发生在哪个CPU -- itid:时间对应哪个utid +- itid:时间对应哪个itid,对应thread表中id ### sched_slice表 #### 表结构 | Columns Name | SQL TYPE | |---- |---- | |id |INT | -|type |TEXT | |ts |INT | |dur |INT | |ts_end |INT | |cpu |INT | |itid |INT | +|ipid |INT | |end_state |TEXT | |priority |INT | +|argset_id |INT | #### 表描述 此数据结构主要作为ThreadState的上下文使用,这张表是sched_switch事件的原始记录。 #### 主要字段描述 -- ts:事件发生事件 -- type:固定字段(sched_slice) +- id: 唯一标识 +- ts:事件发生时间 - dur:状态持续时长 - ts_end:状态结束时长 - cpu:事件发生在哪个cpu -- itid:事件对应哪个utid +- itid:进程内部编号 +- ipid:进程内部编号 - end_state:线程的终结状态 +- priority: 线程优先级 +- argset_id:线程状态参数,对应arg_view中arg_set ### smaps表 #### 表结构 @@ -1340,11 +1402,17 @@ js_heap_sample:记录timeline的时间轴信息 |virtaul_size |INT | |reside |REAL | |protection_id |INT | -|path_id |INT | -#### 表描述 -记录进程的内存消耗的相关信息采样。 +|shared_clean |INT | +|shared_dirty |INT | +|private_clean |INT | +|private_dirty |INT | +|swap |INT | +|swap_pss |INT | +|type |INT | +#### 表描述 +记录进程的内存消耗的相关信息采样,读取/proc/${pid}/smaps节点。 #### 主要字段描述 -- id:状态持续时长 +- id: 唯一标识 - timestamp:事件发生事件 - start_addr:内存段地址的起始位置 - end_addr:内存段地址的结束位置 @@ -1356,6 +1424,14 @@ js_heap_sample:记录timeline的时间轴信息 - reside:实际分配的内存大小与虚拟内存空间的大小的比 - protection_id:内存段的权限id与表data_dict的id字段相关联 - path_id:如果区域是从文件映射的,则这是文件的名称对应的id序号与表data_dict的id字段相关联 +- shared_clean:smaps节点中Shared_clean +- shared_dirty:smaps节点中Shared_dirty +- private_clean:smaps节点中Private_clean +- private_dirty:samps节点中Private_dirty +- swap: smap节点中Swap +- swap_pss:smap节点中SwapPss +- type : 根据type分类信息 + ### stat表 #### 表结构 @@ -1385,8 +1461,9 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 此表记录了被调用函数与其地址的映射关系。 #### 相关字段描述 +- id: 唯一标识 - funcname:系统调用名称 -- adr:系统调用地址 +- addr:系统调用地址 ### syscall表 #### 表结构 @@ -1416,6 +1493,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录所有的filter。 #### 相关字段描述 +- id: 与measure表的filterid字段关联 - type:文件类型 - name:文件名 @@ -1430,7 +1508,9 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录系统内存与系统虚拟内存。 #### 相关字段描述 +- type: 固定为measure - ts:事件时间 +- dur: 持续时间 - value:数值 - filter_id:对应filter表中的ID @@ -1451,7 +1531,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 记录了线程相关数据。 #### 字段详细描述 -- id:线程在数据库重新重新定义的id,从0开始序列增长 +- id: 唯一标识 - itid:TS内部线程id - type:固定字段(thread) - tid:线程号 @@ -1491,10 +1571,11 @@ js_heap_sample:记录timeline的时间轴信息 |tid |INT | |pid |INT | |state |TEXT | +|argset_id |INT | #### 表描述 记录了线程状态相关的数据。 -#### 字段详细描述 -- id:线程状态在数据库中的id,从0开始序列增长 +#### 字段详细描述 +- id: 唯一标识 - ts:该线程状态的起始时间 - dur:该线程状态的持续时间 - cpu:该线程在哪个cpu上执行(针对running状态的线程) @@ -1502,6 +1583,8 @@ js_heap_sample:记录timeline的时间轴信息 - tid:线程号 - pid:进程号 - state:线程实际的的状态值 +- argset_id:线程状态参数,对应arg_view中arg_set + ``` 'R', Runnable状态 "S", interruptible sleep @@ -1567,17 +1650,23 @@ js_heap_sample:记录timeline的时间轴信息 |src |TEXT | |dst |INT | |type |INT | +|type_desc |TEXT | |flag |INT | |depth |INT | |frame_no |INT| #### 表描述 应用的实际渲染帧和期望渲染帧的开始时间,持续时长,以及RenderService和App之间的关联关系。 #### 关键字段描述 +- ts: 数据上报时间戳 +- vsync: 一个id值,用于标识一组渲染帧的期望和实际数据。 +- ipid:所属的进程内部id, 关联process表中的id +- itid:所属的线程id, 关联thread表中的id - callstack_id:该帧数据对应着callstack表的调用栈所在的行数 - dur:该帧渲染时长(当数据不完整时,改行数据为空) - src:该帧是被哪一帧(该表中对应的行数)触发的,有多个值时,用逗号分割 - dst:该帧对应的渲染帧是哪一行 - type: 0 说明该行数据是实际渲染帧, 1 说明该行数据是期望渲染帧 +- type_desc: 当type值为0时,该字段为actural; 当type值为1时,该字段为expect; - flag: 空时,为不完整的数据;0 表示实际渲染帧不卡帧, 1 表示实际渲染帧卡帧(expectEndTime < actualEndTime为异常), 2 表示数据不需要绘制(没有frameNum信息),3 表示rs进程与app进程起止异常(|expRsStartTime - expUiEndTime| < 1ms 正常,否则异常。这里使用期待帧的时间差做判断,给实际帧打标签) - depth:预留 - frame_no:预留 @@ -1630,7 +1719,7 @@ js_heap_sample:记录timeline的时间轴信息 |allocation_itid |INT | |execute_itid |INT | |return_itid |INT | -|execute_id |INT | +|task_id |INT | |priority |INT | |execute_state |INT | |return_state |INT | @@ -1638,13 +1727,14 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 该表记录了任务池相关数据,与callstack表关联。 #### 关键字段描述 +- id: 唯一标识 - allocation_task_row:与callstack表id号相关联 - execute_task_row:与callstack表id号相关联 - return_task_row:与callstack表id号相关联 - allocation_itid:任务分发的itid - execute_itid:任务执行的itid - return_itid:任务返回的itid -- execute_id:任务执行id +- task_id:任务执行id - priority:任务分发独有的,优先级{HIGH : 0,MEDIUM : 1,LOW : 2} - execute_state:任务执行独有的执行状态{NOT_FOUND : 0,WAITING : 1,RUNNING : 2,CANCELED : 3} - return_state:任务返回独有的任务返回状态[IsCanceled DeserializeFailed Successful Unsuccessful] @@ -1663,11 +1753,12 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 该表记录动效的响应时延和完成时延等信息。 #### 关键字段描述 +- id: 唯一标识 - input_time:输入时间点 - start_point:开始时间点 - end_point:结束时间点 -- frame_info:动效帧信息,格式:`实际帧个数:实际帧帧率` -- name: 当前动效名,eg:`H:APP_LIST_FLING, com.taobao.taobao` +- frame_info:动效帧信息,格式:`实际帧个数:实际帧帧率` +- name: 当前动效名,eg:`H:APP_LIST_FLING, com.taobao.taobao` ### dynamic_frame表 #### 表结构 @@ -1684,6 +1775,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 该表记录动效帧的坐标、分辨率、结束时间等信息。 #### 关键字段描述 +- id: 唯一标识 - x:坐标x - y:坐标y - width:宽 @@ -1703,6 +1795,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 该表记录设备分辨率和帧率等信息。 #### 关键字段描述 +- id: 唯一标识 - physical_width:设备宽 - physical_height:设备高 - physical_frame_rate:设备帧率 @@ -1733,22 +1826,29 @@ js_heap_sample:记录timeline的时间轴信息 |recording |INT | |stream_all |INT | #### 表描述 -该表记录设备屏幕亮度,蓝牙,位置,wifi,音乐,媒体等信息。 +该表记录设备屏幕亮度,蓝牙,位置,wifi,音乐,媒体等信息。该表目前暂未被使用。 #### 关键字段描述 +- id: 唯一标识 - brightness:屏幕亮度 - bt_state:蓝牙状态 - location:位置信息 - wifi:无线网络状态 +- stream_default: 取自AudioVolumeInfo message的stream_default字段。 - voice_call:语音通话 - music:音乐播放 +- stream_ring: 取自AudioVolumeInfo message的stream_ring字段。 - media:多媒体 - voice_assistant:语音助手 - system:系统 - alarm:闹钟 - notification:消息通知 - bt_sco:蓝牙语音 +- enforced_audible: 取自AudioVolumeInfo message的enforced_audible字段 +- stream_dtmf: 取自AudioVolumeInfo message的stream_dtmf字段 +- stream_tts: 取自AudioVolumeInfo message的stream_tts字段 - accessibility:访问权限 - recording:录音 +- stream_all: 取自AudioVolumeInfo message的stream_all字段 ### trace_config表 #### 表结构 @@ -1761,6 +1861,7 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 该表记录trace数据源,proto的事件-plugin与其process_name(目前只有HisysEvent事件在用)。 #### 关键字段描述 +- id: 唯一标识 - trace_source:事件源 - key:事件需要关注的信息名 - value:事件需要关注的信息名对应的信息值 @@ -1783,16 +1884,20 @@ js_heap_sample:记录timeline的时间轴信息 |purged |INT | |flag |INT | #### 表描述 -该表记录trace数据源,proto的事件-plugin与其process_name(目前只有HisysEvent事件在用)。 +该表记录trace数据源/proc/purgeable_ashmem_trigger,proto的事件-plugin与其process_name(目前只有HisysEvent事件在用)。 #### 关键字段描述 +- id: 唯一标识 - ts:时间戳 - ipid:内部进程号 +- adj: purgeable_ashmem_trigger中adj - fd:共享内存文件描述符 - ashmem_name_id:共享内存名 - size:共享内存大小 - pss:PSS内存大小 - ashmem_id:共享内存ID +- time: purgeable_ashmem_trigger中time - ref_count:引用计数 +- purged: purgeable_ashmem_trigger中purged - flag:去重标记,0表示正常,1表示进程内部重复数据,2表示进程间重复数据 ### memory_dma表 @@ -1811,13 +1916,16 @@ js_heap_sample:记录timeline的时间轴信息 |exp_name_id |INT | |flag |INT | #### 表描述 -该表记录trace数据源,proto的事件-plugin与其process_name(目前只有HisysEvent事件在用)。 +该表记录trace数据源取/proc/process_dmabuf_info节点,proto的事件-plugin与其process_name(目前只有HisysEvent事件在用)。 #### 关键字段描述 +- id: 唯一标识 - ts:时间戳 - ipid:内部进程号 - fd:dma内存文件描述符 - size:dma内存大小 +- ino: process_dmabuf_info中ino列 - exp_pid:申请者的进程号 +- exp_task_comm_id:申请者的的线程名,对应data_dict的id - buf_name_id:dma内存名 - exp_name_id:申请者进程名 - flag:去重标记,0表示正常,1表示进程内部重复数据,2表示进程间重复数据 @@ -1835,8 +1943,9 @@ js_heap_sample:记录timeline的时间轴信息 |itid |INT | |used_gpu_size |INT | #### 表描述 -该表记录trace数据源,proto的事件-plugin与其process_name(目前只有HisysEvent事件在用)。 +该表记录trace数据源读取/proc/gpu_memory节点 #### 关键字段描述 +- id: 唯一标识 - ts:时间戳 - gpu_name_id:gpu内存名称 - all_gpu_size:进程占用gpu总大小 @@ -1859,15 +1968,17 @@ js_heap_sample:记录timeline的时间轴信息 |count |INT | |purgeable_size|INT | #### 表描述 -该表记录trace数据源,proto的事件-plugin与其process_name(目前只有HisysEvent事件在用)。 +该表记录trace数据源/sys/kernel/debug/mali0/ctx/$(pidof xxx)_0/mem_profile #### 关键字段描述 +- id: 唯一标识 - ts:时间戳 - window_name_id:窗口名 - window_id:窗口id - module_name_id:模块名 - category_name_id:目录名 -- size:内存大小 +- size:内存大小 bytes - count:内存申请个数 +- purgeable_size: 取Total memory对应的字节大小 ### static_initalize表 #### 表结构 @@ -1884,10 +1995,99 @@ js_heap_sample:记录timeline的时间轴信息 #### 表描述 该表记录了so初始化相关数据。 #### 关键字段描述 +- id: 唯一标识 - ipid:内部进程号 - tid:内部线程号 - call_id:调用者的ID,对应线程表里面的itid - start_time:阶段开始时间 - end_time:阶段结束时间 - so_name:so文件名称 -- depth:泳道图的深度 \ No newline at end of file +- depth:泳道图的深度 +### memory_cpu表 +#### 表结构 +| Columns Name | SQL TYPE | +|---- |---- | +|id |INT | +|ts |INT | +|total_size |INT | +#### 表描述 +该表记录了hidumper抓取的cpu的内存大小的相关数据。 +#### 关键字段描述 +- id: 唯一标识 +- ts:数据上报时间戳 +- total_size:hidumper取到的cpu内存大小 + +### memory_profile表 +#### 表结构 +| Columns Name | SQL TYPE | +|---- |---- | +|id |INT | +|ts |INT | +|channel_id |INT | +|total_size |INT | +#### 表描述 +该表记录了读取/sys/kernel/debug/mali0/ctx/$(pidof xxx)_0/mem_profile节点相关数据。 +#### 关键字段描述 +- id: 唯一标识 +- ts:数据上报时间戳 +- channel_id: 取Channel对应的名称,对应data_dict的id +- total_size:取Total memory对应的字节大小 + +### memory_rs_image表 +#### 表结构 +| Columns Name | SQL TYPE | +|---- |---- | +|id |INT | +|ts |INT | +|ipid |INT | +|mem_size |INT | +|type_id |INT | +|surface_name_id|INT | + +#### 表描述 +该表记录了hidumper抓取的界面的内存大小的相关数据。 +#### 关键字段描述 +- id: 唯一标识 +- ts:数据上报时间戳 +- ipid:内部进程号 +- mem_size: 取hidumper的size列 +- type_id:取hidumper的type列,对于data_dict表中的id +- surface_name_id: 取hidumper的surfaceName列 +### hisys_all_event表 +#### 表结构 +| Columns Name | SQL TYPE | +|---- |---- | +|id |INT | +|domain_id |INT | +|event_name_id |INT | +|ts |INT | +|type |INT | +|time_zone |TEXT | +|pid |INT | +|tid |INT | +|uid |INT | +|level |TEXT | +|tag |TEXT | +|event_id |INT | +|seq |INT | +|info |TEXT | +|contents |TEXT | + +#### 表描述 +该表记录所有hisysevent采集到的原始数据。 +#### 关键字段描述 +- id: 唯一标识一条该表数据 +- domain_id: 对应原始数据中的domain_字段在data_dict表中的索引。 +- event_name_id: 对应原始数据中name_字段在data_dict表中的索引。 +- ts: 对应原始数据中time_(ms)字段转化成ns +- type: 对应原始数据中type_字段 +- time_zone: 对应原始数据中tz_字段 +- pid: 进程号,对应原始数据中的pid_ +- tid: 线程号, 对应原始数据中tid_ +- uid: 对应原始数据中uid_ +- level: 对应原始数据中level_ +- tag: 对应原始数据中tag_ +- event_id: 对应原始数据id_ +- seq: 对应原始数据中seq_ +- info: 对应原数据中info_字段 +- contents: 取源数据中除了以上字段外的其他字段,组成新的json数据。 \ No newline at end of file diff --git a/trace_streamer/figures/db_hisys_event.png b/trace_streamer/figures/db_hisys_event.png index 215265182de684f1b4a31fd7158309f8c7532053..5d2fb43e681e6cfdd1ec5d08f81d21c36c9bc088 100755 GIT binary patch literal 32425 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%0A({i3F@I(L=+t!{aUd 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 diff --git a/trace_streamer/figures/db_native_memory.png b/trace_streamer/figures/db_native_memory.png index 7cb32fd4250fcda94ad9aa86157d02628e776e28..c0733454d252c57121f2986af0ab8c0f7189c8b5 100644 GIT binary patch literal 38333 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�q4*`DiHDb0753&J-^ov(J|VFHq>#FLb`96B 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 diff --git a/trace_streamer/figures/dump_and_mem.png b/trace_streamer/figures/dump_and_mem.png index ab9d01a8107c957e53cce9b66ff5a1d8e200064c..5d66800f663a7dcc66538363b794e42afe816340 100644 GIT binary patch literal 13102 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 literal 8417 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 diff --git a/trace_streamer/pare_third_party.sh b/trace_streamer/pare_third_party.sh index 4fa45c3c1..74bea958d 100755 --- a/trace_streamer/pare_third_party.sh +++ b/trace_streamer/pare_third_party.sh @@ -132,8 +132,6 @@ if [ ! -f "hiperf/BUILD.gn" ];then if [ -d "developtools_hiperf" ];then mv developtools_hiperf hiperf $cp ../prebuilts/patch_hiperf/BUILD.gn ../third_party/hiperf/BUILD.gn - $cp ../prebuilts/patch_hiperf/file_ex.h hiperf/include/nonlinux/linux - $cp ../prebuilts/patch_hiperf/unique_fd.h hiperf/include/nonlinux/linux $sed -i "/FRIEND_TEST/s/^\(.*\)$/\/\/\1/g" hiperf/include/virtual_thread.h $sed -i "s/HIPERF_DEBUG/ALWAYSTRUE/g" hiperf/include/virtual_thread.h $sed -i "/#include \"report_json_file.h\"/s/^\(.*\)$/\/\/\1/g" hiperf/include/report.h @@ -168,12 +166,11 @@ if [ ! -f "bounds_checking_function/BUILD.gn" ];then $cp ../prebuilts/patch_bounds_checking_function/bounds_checking_functionbuild.gn bounds_checking_function/BUILD.gn fi -if [ ! -f "commonlibrary/c_utils/base/include/nocopyable.h" ];then +if [ ! -d "commonlibrary" ];then rm -rf commonlibrary git clone --depth=1 git@gitee.com:openharmony/commonlibrary_c_utils.git if [ -d "commonlibrary_c_utils" ];then - mkdir -p commonlibrary/c_utils/base/include - $cp commonlibrary_c_utils/base/include/nocopyable.h commonlibrary/c_utils/base/include + mv commonlibrary_c_utils commonlibrary rm -rf commonlibrary_c_utils fi fi diff --git a/trace_streamer/prebuilts/patch_hiperf/BUILD.gn b/trace_streamer/prebuilts/patch_hiperf/BUILD.gn index 4fbceafea..4fcf8600e 100644 --- a/trace_streamer/prebuilts/patch_hiperf/BUILD.gn +++ b/trace_streamer/prebuilts/patch_hiperf/BUILD.gn @@ -13,87 +13,66 @@ import("//build/ohos.gni") import("../../build/ts.gni") -ohos_source_set("elf") { - subsystem_name = "developtools" - part_name = "smartperf_host" - configs -= [ trace_cfg_path ] - configs += [ "../../gn:hiperf_trace_cfg" ] - cflags = [ "-D is_mingw=${is_mingw}" ] - sources = [ "src/utilities.cpp" ] - include_dirs = [ - "include", - "${SRC}/base", - "include/nonlinux/linux", - "include/nonlinux", - "${THIRD_PARTY}/bounds_checking_function/include", +config("hiperf_config") { + cflags = [ + "-D ALWAYSTRUE", + "-D is_mingw=${is_mingw}", ] - public_deps = [ "${THIRD_PARTY}/zlib:libz" ] } -ohos_source_set("hiperf_src") { - configs -= [ trace_cfg_path ] - configs += [ "../../gn:hiperf_trace_cfg" ] - subsystem_name = "developtools" - part_name = "smartperf_host" - cflags = [ "-D ALWAYSTRUE" ] - if (is_mingw) { - cflags += [ "-includeMingW64Fix.h" ] - } - sources = [ - "./src/callstack.cpp", - "./src/callstack.h", - "./src/dwarf_encoding.cpp", - "./src/dwarf_encoding.h", - "./src/hashlist.h", - "./src/option.cpp", - "./src/perf_event_record.cpp", - "./src/perf_file_format.cpp", - "./src/perf_file_reader.cpp", - "./src/register.cpp", - "./src/register.h", - "./src/report.cpp", - "./src/subcommand.cpp", - "./src/symbols_file.cpp", - "./src/unique_stack_table.cpp", - "./src/virtual_runtime.cpp", - "./src/virtual_thread.cpp", - "include/symbols_file.h", - ] +config("hiperf_public_config") { include_dirs = [ "linux", - "../", - "./", - "../../src/base", "include", - "../../src/include", - "../../src/", - "../../src/trace_streamer", "include/nonlinux", "include/nonlinux/linux", + "${SRC}/", + "${SRC}/base", + "${SRC}/include", + "${SRC}/trace_streamer", "${THIRD_PARTY}/bounds_checking_function/include", - ] - include_dirs += [ "${THIRD_PARTY}/perf_include/libbpf", - "${THIRD_PARTY}/libunwind/include", - "${THIRD_PARTY}/libunwind/src", - "${THIRD_PARTY}/libunwind/include/tdep-x86_64", - ] - include_dirs += [ - "${THIRD_PARTY}/perf_include/hiviewdfx/hilog/include", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/cutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxlog", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/hilog/include", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/common", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/nonlinux", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/include", + "${COMMON_LIBRARY}/base/include", ] + if (is_mingw) { + cflags = [ "-includeMingW64Fix.h" ] + } } -group("hiperf_platform_common") { - deps = [ - ":elf", - ":hiperf_src", - "${THIRD_PARTY}/protobuf:protobuf_lite_static", - "${THIRD_PARTY}/protobuf:protobuf_static", - "//third_party/perf_include/hiviewdfx:hiviewdfx", +ohos_source_set("hiperf_src") { + subsystem_name = "thirdparty" + part_name = "hiperf" + sources = [ + "src/callstack.cpp", + "src/dwarf_encoding.cpp", + "src/option.cpp", + "src/perf_event_record.cpp", + "src/perf_file_format.cpp", + "src/perf_file_reader.cpp", + "src/register.cpp", + "src/report.cpp", + "src/subcommand.cpp", + "src/symbols_file.cpp", + "src/unique_stack_table.cpp", + "src/utilities.cpp", + "src/virtual_runtime.cpp", + "src/virtual_thread.cpp", + ] + configs -= [ trace_cfg_path ] + configs += [ "../../gn:hiperf_trace_cfg" ] + configs += [ ":hiperf_config" ] + public_configs = [ ":hiperf_public_config" ] + public_deps = [ + "${THIRD_PARTY}/zlib:libz", + "//third_party/perf_include/hiviewdfx:libfaultloggerd", ] + if (!use_wasm && !is_win && !is_mingw && !is_mac && !is_test) { + if (!is_independent_compile) { + if (target_cpu == "arm64") { + public_deps += [ "//third_party/libunwind:unwind_source_arm64_opt" ] + } else { + public_deps += [ "//third_party/libunwind:unwind_source_${target_cpu}" ] + } + } else { + public_deps += [ "${THIRD_PARTY}/libunwind:libunwind" ] + } + } } diff --git a/trace_streamer/prebuilts/patch_hiperf/hiviewdfx_BUILD.gn b/trace_streamer/prebuilts/patch_hiperf/hiviewdfx_BUILD.gn index ca28797f0..7e9651695 100644 --- a/trace_streamer/prebuilts/patch_hiperf/hiviewdfx_BUILD.gn +++ b/trace_streamer/prebuilts/patch_hiperf/hiviewdfx_BUILD.gn @@ -12,55 +12,48 @@ # limitations under the License. import("//build/ohos.gni") import("//build/ts.gni") -ohos_source_set("hiviewdfx_source") { - configs -= [ trace_cfg_path ] - configs += [ "${TS_DIR}/gn:hiperf_trace_cfg" ] - subsystem_name = "developtools" - part_name = "smartperf_host" - cflags = [ "-D ALWAYSTRUE" ] - cflags += [ "-D DFX_NO_PRINT_LOG" ] - cflags += [ "-D is_host" ] +config("faultloggerd_config") { + cflags = [ + "-D ALWAYSTRUE", + "-D DFX_NO_PRINT_LOG", + "-D is_host", + ] if (!is_independent_compile) { configs = [ "${TS_DIR}/gn:ts_config" ] } - include_dirs = [ "${THIRD_PARTY}/perf_include/hiviewdfx/hilog/include" ] - include_dirs += [ "${THIRD_PARTY}/bounds_checking_function/include" ] - include_dirs += [ - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxlog", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/hilog/include", - ] - sources = [ "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxutil/dfx_util.cpp" ] - include_dirs += [ - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/common", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/nonlinux", - ] - - include_dirs += [ "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/include" ] - include_dirs += [ +} +config("faultloggerd_public_config") { + include_dirs = [ + "faultloggerd/common/dfxlog", + "faultloggerd/common/dfxutil", + "faultloggerd/interfaces/common", + "faultloggerd/interfaces/nonlinux", + "faultloggerd/interfaces/innerkits/unwinder/include", "${THIRD_PARTY}/hiperf/include", "${THIRD_PARTY}/hiperf/include/nonlinux", "${THIRD_PARTY}/hiperf/include/nonlinux/linux", + "${THIRD_PARTY}/bounds_checking_function/include", + "${COMMON_LIBRARY}/base/include", ] if (is_mingw) { - cflags += [ "-includeMingW64Fix.h" ] include_dirs += [ "${THIRD_PARTY}/libunwind/include/mingw" ] } - sources += [ - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_elf.cpp", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_elf_parser.cpp", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_map.cpp", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_maps.cpp", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_memory.cpp", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_mmap.cpp", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/dfx_symbols.cpp", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/unwinder_config.cpp", - ] } - -group("hiviewdfx") { - deps = [ - ":hiviewdfx_source", - "${THIRD_PARTY}/bounds_checking_function:libsec_static", +ohos_source_set("libfaultloggerd") { + sources = [ + "faultloggerd/common/dfxutil/dfx_util.cpp", + "faultloggerd/interfaces/innerkits/unwinder/dfx_elf.cpp", + "faultloggerd/interfaces/innerkits/unwinder/dfx_elf_parser.cpp", + "faultloggerd/interfaces/innerkits/unwinder/dfx_map.cpp", + "faultloggerd/interfaces/innerkits/unwinder/dfx_maps.cpp", + "faultloggerd/interfaces/innerkits/unwinder/dfx_memory.cpp", + "faultloggerd/interfaces/innerkits/unwinder/dfx_mmap.cpp", + "faultloggerd/interfaces/innerkits/unwinder/dfx_symbols.cpp", + "faultloggerd/interfaces/innerkits/unwinder/unwinder_config.cpp", ] + configs += [ ":faultloggerd_config" ] + public_configs = [ ":faultloggerd_public_config" ] + public_deps = [ "${THIRD_PARTY}/bounds_checking_function:libsec_static" ] + part_name = "faultloggerd" + subsystem_name = "thirdparty" } diff --git a/trace_streamer/prebuilts/patch_hiperf/unique_fd.h b/trace_streamer/prebuilts/patch_hiperf/unique_fd.h deleted file mode 100644 index d4ad56409..000000000 --- a/trace_streamer/prebuilts/patch_hiperf/unique_fd.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef UNIQUE_FD_H -#define UNIQUE_FD_H - -#include - -namespace OHOS { -class DefaultDeleter { -public: - static void Close(int fd) - { - if (fd >= 0) { - close(fd); - } - } -}; - -template -class UniqueFdAddDeletor; -template -bool operator==(const int &lhs, const UniqueFdAddDeletor &rhs); -template -bool operator!=(const int &lhs, const UniqueFdAddDeletor &rhs); -template -bool operator>=(const int &lhs, const UniqueFdAddDeletor &rhs); -template -bool operator>(const int &lhs, const UniqueFdAddDeletor &rhs); -template -bool operator<=(const int &lhs, const UniqueFdAddDeletor &rhs); -template -bool operator<(const int &lhs, const UniqueFdAddDeletor &rhs); - -template -class UniqueFdAddDeletor final { - friend bool operator==(const int &lhs, const UniqueFdAddDeletor &rhs); - - friend bool operator!=(const int &lhs, const UniqueFdAddDeletor &rhs); - - friend bool operator>=(const int &lhs, const UniqueFdAddDeletor &rhs); - - friend bool operator>(const int &lhs, const UniqueFdAddDeletor &rhs); - - friend bool operator<=(const int &lhs, const UniqueFdAddDeletor &rhs); - // clang-format off - friend bool operator< (const int& lhs, const UniqueFdAddDeletor& rhs); - // clang-format on -public: - explicit UniqueFdAddDeletor(const int &value) : fd_(value) {} - UniqueFdAddDeletor() : fd_(-1) {} - ~UniqueFdAddDeletor() - { - Reset(-1); - } - - // get fd out - int Release() - { - int tmp = fd_; - fd_ = -1; - return tmp; - } - - // this is dangerous, when you use it , you should know it, donot operator on the ret - operator int() const - { - return Get(); - } // NOLINT - // this is dangerous, when you use it , you should know it, donot operator on the ret - int Get() const - { - return fd_; - } - - // we need move fd from one to another - UniqueFdAddDeletor(UniqueFdAddDeletor &&rhs) - { - int rhsfd = rhs.Release(); - fd_ = rhsfd; - } - - UniqueFdAddDeletor &operator=(UniqueFdAddDeletor &&rhs) - { - int rhsfd = rhs.Release(); - Reset(rhsfd); - return *this; - } - - bool operator==(const int &rhs) const - { - return fd_ == rhs; - } - - bool operator!=(const int &rhs) const - { - return !(fd_ == rhs); - } - bool operator>=(const int &rhs) const - { - return fd_ >= rhs; - } - - bool operator>(const int &rhs) const - { - return fd_ > rhs; - } - - bool operator<=(const int &rhs) const - { - return fd_ <= rhs; - } - - bool operator<(const int &rhs) const - { - return fd_ < rhs; - } - -private: - int fd_ = -1; - - void Reset(int newValue) - { - if (fd_ >= 0) { - Deleter::Close(fd_); - } - fd_ = newValue; - } - - // disallow copy ctor and copy assign - UniqueFdAddDeletor(const UniqueFdAddDeletor &rhs) = delete; - UniqueFdAddDeletor &operator=(const UniqueFdAddDeletor &rhs) = delete; -}; - -template -bool operator==(const int &lhs, const UniqueFdAddDeletor &uniqueFd) -{ - return lhs == uniqueFd.fd_; -} - -template -bool operator!=(const int &lhs, const UniqueFdAddDeletor &uniqueFd) -{ - return !(lhs == uniqueFd.fd_); -} - -template -bool operator>=(const int &lhs, const UniqueFdAddDeletor &uniqueFd) -{ - return lhs >= uniqueFd.fd_; -} - -template -bool operator>(const int &lhs, const UniqueFdAddDeletor &uniqueFd) -{ - return lhs > uniqueFd.fd_; -} - -template -bool operator<=(const int &lhs, const UniqueFdAddDeletor &uniqueFd) -{ - return lhs <= uniqueFd.fd_; -} - -template -bool operator<(const int &lhs, const UniqueFdAddDeletor &uniqueFd) -{ - return lhs < uniqueFd.fd_; -} - -using UniqueFd = UniqueFdAddDeletor; -} // namespace OHOS -#endif diff --git a/trace_streamer/prebuilts/patch_libunwind/libunwindbuild.gn b/trace_streamer/prebuilts/patch_libunwind/libunwindbuild.gn index 5228ee192..2d9d555a6 100644 --- a/trace_streamer/prebuilts/patch_libunwind/libunwindbuild.gn +++ b/trace_streamer/prebuilts/patch_libunwind/libunwindbuild.gn @@ -13,225 +13,221 @@ import("//build/ohos.gni") common_source = [ - "src/dwarf/Gexpr.c", - "src/dwarf/Gfde.c", - "src/dwarf/Gfind_proc_info-lsb.c", - "src/dwarf/Gfind_unwind_table.c", - "src/dwarf/global.c", - "src/dwarf/Gparser.c", - "src/dwarf/Gpe.c", - "src/dwarf/Lexpr.c", - "src/dwarf/Lfde.c", - "src/dwarf/Lfind_proc_info-lsb.c", - "src/dwarf/Lfind_unwind_table.c", - "src/dwarf/Lparser.c", - "src/dwarf/Lpe.c", - "src/mi/backtrace.c", - "src/mi/dyn-cancel.c", - "src/mi/dyn-info-list.c", - "src/mi/dyn-register.c", - "src/mi/flush_cache.c", - "src/mi/Gdestroy_addr_space.c", - "src/mi/Gdyn-extract.c", - "src/mi/Gdyn-remote.c", - "src/mi/Gfind_dynamic_proc_info.c", - "src/mi/Gget_accessors.c", - "src/mi/Gget_fpreg.c", - "src/mi/Gget_proc_info_by_ip.c", - "src/mi/Gget_proc_name.c", - "src/mi/Gget_reg.c", - "src/mi/Gput_dynamic_unwind_info.c", - "src/mi/Gset_cache_size.c", - "src/mi/Gset_caching_policy.c", - "src/mi/Gset_fpreg.c", - "src/mi/Gset_reg.c", - "src/mi/init.c", - "src/mi/Ldestroy_addr_space.c", - "src/mi/Ldyn-extract.c", - "src/mi/Lfind_dynamic_proc_info.c", - "src/mi/Lget_fpreg.c", - "src/mi/Lget_proc_info_by_ip.c", - "src/mi/Lget_proc_name.c", - "src/mi/Lget_reg.c", - "src/mi/Lput_dynamic_unwind_info.c", - "src/mi/Lset_cache_size.c", - "src/mi/Lset_caching_policy.c", - "src/mi/Lset_fpreg.c", - "src/mi/Lset_reg.c", - "src/mi/mempool.c", - "src/mi/strerror.c", - "src/os-linux.c", - "src/ohos-config.c", - "src/ptrace/_UPT_access_fpreg.c", - "src/ptrace/_UPT_access_mem.c", - "src/ptrace/_UPT_access_reg.c", - "src/ptrace/_UPT_accessors.c", - "src/ptrace/_UPT_create.c", - "src/ptrace/_UPT_destroy.c", - "src/ptrace/_UPT_find_proc_info.c", - "src/ptrace/_UPT_get_dyn_info_list_addr.c", - "src/ptrace/_UPT_get_proc_name.c", - "src/ptrace/_UPT_put_unwind_info.c", - "src/ptrace/_UPT_reg_offset.c", - "src/ptrace/_UPT_resume.c", - "src/mi/maps.c", + "libunwind/src/dwarf/Gexpr.c", + "libunwind/src/dwarf/Gfde.c", + "libunwind/src/dwarf/Gfind_proc_info-lsb.c", + "libunwind/src/dwarf/Gfind_unwind_table.c", + "libunwind/src/dwarf/global.c", + "libunwind/src/dwarf/Gparser.c", + "libunwind/src/dwarf/Gpe.c", + "libunwind/src/dwarf/Lexpr.c", + "libunwind/src/dwarf/Lfde.c", + "libunwind/src/dwarf/Lfind_proc_info-lsb.c", + "libunwind/src/dwarf/Lfind_unwind_table.c", + "libunwind/src/dwarf/Lparser.c", + "libunwind/src/dwarf/Lpe.c", + "libunwind/src/mi/backtrace.c", + "libunwind/src/mi/dyn-cancel.c", + "libunwind/src/mi/dyn-info-list.c", + "libunwind/src/mi/dyn-register.c", + "libunwind/src/mi/flush_cache.c", + "libunwind/src/mi/Gdestroy_addr_space.c", + "libunwind/src/mi/Gdyn-extract.c", + "libunwind/src/mi/Gdyn-remote.c", + "libunwind/src/mi/Gfind_dynamic_proc_info.c", + "libunwind/src/mi/Gget_accessors.c", + "libunwind/src/mi/Gget_fpreg.c", + "libunwind/src/mi/Gget_proc_info_by_ip.c", + "libunwind/src/mi/Gget_proc_name.c", + "libunwind/src/mi/Gget_reg.c", + "libunwind/src/mi/Gput_dynamic_unwind_info.c", + "libunwind/src/mi/Gset_cache_size.c", + "libunwind/src/mi/Gset_caching_policy.c", + "libunwind/src/mi/Gset_fpreg.c", + "libunwind/src/mi/Gset_reg.c", + "libunwind/src/mi/init.c", + "libunwind/src/mi/Ldestroy_addr_space.c", + "libunwind/src/mi/Ldyn-extract.c", + "libunwind/src/mi/Lfind_dynamic_proc_info.c", + "libunwind/src/mi/Lget_fpreg.c", + "libunwind/src/mi/Lget_proc_info_by_ip.c", + "libunwind/src/mi/Lget_proc_name.c", + "libunwind/src/mi/Lget_reg.c", + "libunwind/src/mi/Lput_dynamic_unwind_info.c", + "libunwind/src/mi/Lset_cache_size.c", + "libunwind/src/mi/Lset_caching_policy.c", + "libunwind/src/mi/Lset_fpreg.c", + "libunwind/src/mi/Lset_reg.c", + "libunwind/src/mi/mempool.c", + "libunwind/src/mi/strerror.c", + "libunwind/src/os-linux.c", + "libunwind/src/ptrace/_UPT_access_fpreg.c", + "libunwind/src/ptrace/_UPT_access_mem.c", + "libunwind/src/ptrace/_UPT_access_reg.c", + "libunwind/src/ptrace/_UPT_accessors.c", + "libunwind/src/ptrace/_UPT_create.c", + "libunwind/src/ptrace/_UPT_destroy.c", + "libunwind/src/ptrace/_UPT_find_proc_info.c", + "libunwind/src/ptrace/_UPT_get_proc_name.c", + "libunwind/src/ptrace/_UPT_put_unwind_info.c", + "libunwind/src/ptrace/_UPT_reg_offset.c", + "libunwind/src/ptrace/_UPT_resume.c", ] # as libc++ is static linked with libunwind.a # we remove the Gstep.c for duplicated symbol violation if (target_cpu == "arm") { arm_source = [ - "src/arm/Gapply_reg_state.c", - "src/arm/Gcreate_addr_space.c", - "src/arm/Gex_tables.c", - "src/arm/Gget_proc_info.c", - "src/arm/Gget_save_loc.c", - "src/arm/Gglobal.c", - "src/arm/Ginit.c", - "src/arm/Ginit_local.c", - "src/arm/Ginit_remote.c", - "src/arm/Gos-linux.c", - "src/arm/Greg_states_iterate.c", - "src/arm/Gregs.c", - "src/arm/Gresume.c", - "src/arm/Gstash_frame.c", - "src/arm/Gstep.c", - "src/arm/Gtrace.c", - "src/arm/Lcreate_addr_space.c", - "src/arm/Lex_tables.c", - "src/arm/Lget_proc_info.c", - "src/arm/Lget_save_loc.c", - "src/arm/Lglobal.c", - "src/arm/Linit.c", - "src/arm/Linit_local.c", - "src/arm/Linit_remote.c", - "src/arm/Los-linux.c", - "src/arm/Lregs.c", - "src/arm/Lresume.c", - "src/arm/Lstash_frame.c", - "src/arm/Lstep.c", - "src/arm/Ltrace.c", - "src/arm/gen-offsets.c", - "src/arm/getcontext.S", - "src/arm/is_fpreg.c", - "src/arm/regname.c", - "src/arm/siglongjmp.S", - "src/elf32.c", + "libunwind/src/arm/Gapply_reg_state.c", + "libunwind/src/arm/Gcreate_addr_space.c", + "libunwind/src/arm/Gex_tables.c", + "libunwind/src/arm/Gget_proc_info.c", + "libunwind/src/arm/Gget_save_loc.c", + "libunwind/src/arm/Gglobal.c", + "libunwind/src/arm/Ginit.c", + "libunwind/src/arm/Ginit_local.c", + "libunwind/src/arm/Ginit_remote.c", + "libunwind/src/arm/Gos-linux.c", + "libunwind/src/arm/Greg_states_iterate.c", + "libunwind/src/arm/Gregs.c", + "libunwind/src/arm/Gresume.c", + "libunwind/src/arm/Gstash_frame.c", + "libunwind/src/arm/Gstep.c", + "libunwind/src/arm/Gtrace.c", + "libunwind/src/arm/Lcreate_addr_space.c", + "libunwind/src/arm/Lex_tables.c", + "libunwind/src/arm/Lget_proc_info.c", + "libunwind/src/arm/Lget_save_loc.c", + "libunwind/src/arm/Lglobal.c", + "libunwind/src/arm/Linit.c", + "libunwind/src/arm/Linit_local.c", + "libunwind/src/arm/Linit_remote.c", + "libunwind/src/arm/Los-linux.c", + "libunwind/src/arm/Lregs.c", + "libunwind/src/arm/Lresume.c", + "libunwind/src/arm/Lstash_frame.c", + "libunwind/src/arm/Lstep.c", + "libunwind/src/arm/Ltrace.c", + "libunwind/src/arm/getcontext.S", + "libunwind/src/arm/is_fpreg.c", + "libunwind/src/arm/regname.c", + "libunwind/src/arm/siglongjmp.S", + "libunwind/src/elf32.c", ] } if (target_cpu == "arm64") { arm64_source = [ - "src/aarch64/Gcreate_addr_space.c", - "src/aarch64/Gget_proc_info.c", - "src/aarch64/Gget_save_loc.c", - "src/aarch64/Gglobal.c", - "src/aarch64/Ginit.c", - "src/aarch64/Ginit_local.c", - "src/aarch64/Ginit_remote.c", - "src/aarch64/Gis_signal_frame.c", - "src/aarch64/Gregs.c", - "src/aarch64/Gresume.c", - "src/aarch64/Gstash_frame.c", - "src/aarch64/Gstep.c", - "src/aarch64/Gtrace.c", - "src/aarch64/Lcreate_addr_space.c", - "src/aarch64/Lget_proc_info.c", - "src/aarch64/Lget_save_loc.c", - "src/aarch64/Lglobal.c", - "src/aarch64/Linit.c", - "src/aarch64/Linit_local.c", - "src/aarch64/Linit_remote.c", - "src/aarch64/Lis_signal_frame.c", - "src/aarch64/Lregs.c", - "src/aarch64/Lresume.c", - "src/aarch64/Lstash_frame.c", - "src/aarch64/Lstep.c", - "src/aarch64/Ltrace.c", - "src/aarch64/getcontext.S", - "src/aarch64/is_fpreg.c", - "src/aarch64/regname.c", - "src/elf64.c", + "libunwind/src/aarch64/Gcreate_addr_space.c", + "libunwind/src/aarch64/Gget_proc_info.c", + "libunwind/src/aarch64/Gget_save_loc.c", + "libunwind/src/aarch64/Gglobal.c", + "libunwind/src/aarch64/Ginit.c", + "libunwind/src/aarch64/Ginit_local.c", + "libunwind/src/aarch64/Ginit_remote.c", + "libunwind/src/aarch64/Gis_signal_frame.c", + "libunwind/src/aarch64/Gregs.c", + "libunwind/src/aarch64/Gresume.c", + "libunwind/src/aarch64/Gstash_frame.c", + "libunwind/src/aarch64/Gstep.c", + "libunwind/src/aarch64/Gtrace.c", + "libunwind/src/aarch64/Lcreate_addr_space.c", + "libunwind/src/aarch64/Lget_proc_info.c", + "libunwind/src/aarch64/Lget_save_loc.c", + "libunwind/src/aarch64/Lglobal.c", + "libunwind/src/aarch64/Linit.c", + "libunwind/src/aarch64/Linit_local.c", + "libunwind/src/aarch64/Linit_remote.c", + "libunwind/src/aarch64/Lis_signal_frame.c", + "libunwind/src/aarch64/Lregs.c", + "libunwind/src/aarch64/Lresume.c", + "libunwind/src/aarch64/Lstash_frame.c", + "libunwind/src/aarch64/Lstep.c", + "libunwind/src/aarch64/Ltrace.c", + "libunwind/src/aarch64/getcontext.S", + "libunwind/src/aarch64/is_fpreg.c", + "libunwind/src/aarch64/regname.c", + "libunwind/src/elf64.c", ] } if (target_cpu == "x64") { x64_source = [ - "src/elf64.c", - "src/x86_64/Gcreate_addr_space.c", - "src/x86_64/Gget_proc_info.c", - "src/x86_64/Gget_save_loc.c", - "src/x86_64/Gglobal.c", - "src/x86_64/Ginit.c", - "src/x86_64/Ginit_local.c", - "src/x86_64/Ginit_remote.c", - "src/x86_64/Gos-linux.c", - "src/x86_64/Gregs.c", - "src/x86_64/Gresume.c", - "src/x86_64/Gstash_frame.c", - "src/x86_64/Gstep.c", - "src/x86_64/Gtrace.c", - "src/x86_64/Lcreate_addr_space.c", - "src/x86_64/Lget_proc_info.c", - "src/x86_64/Lget_save_loc.c", - "src/x86_64/Lglobal.c", - "src/x86_64/Linit.c", - "src/x86_64/Linit_local.c", - "src/x86_64/Linit_remote.c", - "src/x86_64/Los-linux.c", - "src/x86_64/Lregs.c", - "src/x86_64/Lresume.c", - "src/x86_64/Lstash_frame.c", - "src/x86_64/Lstep.c", - "src/x86_64/Ltrace.c", - "src/x86_64/getcontext.S", - "src/x86_64/is_fpreg.c", - "src/x86_64/regname.c", - "src/x86_64/setcontext.S", + "libunwind/src/elf64.c", + "libunwind/src/x86_64/Gcreate_addr_space.c", + "libunwind/src/x86_64/Gget_proc_info.c", + "libunwind/src/x86_64/Gget_save_loc.c", + "libunwind/src/x86_64/Gglobal.c", + "libunwind/src/x86_64/Ginit.c", + "libunwind/src/x86_64/Ginit_local.c", + "libunwind/src/x86_64/Ginit_remote.c", + "libunwind/src/x86_64/Gos-linux.c", + "libunwind/src/x86_64/Gregs.c", + "libunwind/src/x86_64/Gresume.c", + "libunwind/src/x86_64/Gstash_frame.c", + "libunwind/src/x86_64/Gstep.c", + "libunwind/src/x86_64/Gtrace.c", + "libunwind/src/x86_64/Lcreate_addr_space.c", + "libunwind/src/x86_64/Lget_proc_info.c", + "libunwind/src/x86_64/Lget_save_loc.c", + "libunwind/src/x86_64/Lglobal.c", + "libunwind/src/x86_64/Linit.c", + "libunwind/src/x86_64/Linit_local.c", + "libunwind/src/x86_64/Linit_remote.c", + "libunwind/src/x86_64/Los-linux.c", + "libunwind/src/x86_64/Lregs.c", + "libunwind/src/x86_64/Lresume.c", + "libunwind/src/x86_64/Lstash_frame.c", + "libunwind/src/x86_64/Lstep.c", + "libunwind/src/x86_64/Ltrace.c", + "libunwind/src/x86_64/getcontext.S", + "libunwind/src/x86_64/is_fpreg.c", + "libunwind/src/x86_64/regname.c", + "libunwind/src/x86_64/setcontext.S", ] } remove_sources = [] ptrace_sources = [ - "src/ptrace/_UPT_access_fpreg.c", - "src/ptrace/_UPT_access_mem.c", - "src/ptrace/_UPT_access_reg.c", - "src/ptrace/_UPT_accessors.c", - "src/ptrace/_UPT_create.c", - "src/ptrace/_UPT_destroy.c", - "src/ptrace/_UPT_find_proc_info.c", - "src/ptrace/_UPT_get_dyn_info_list_addr.c", - "src/ptrace/_UPT_get_proc_name.c", - "src/ptrace/_UPT_put_unwind_info.c", - "src/ptrace/_UPT_reg_offset.c", - "src/ptrace/_UPT_resume.c", + "libunwind/src/ptrace/_UPT_access_fpreg.c", + "libunwind/src/ptrace/_UPT_access_mem.c", + "libunwind/src/ptrace/_UPT_access_reg.c", + "libunwind/src/ptrace/_UPT_accessors.c", + "libunwind/src/ptrace/_UPT_create.c", + "libunwind/src/ptrace/_UPT_destroy.c", + "libunwind/src/ptrace/_UPT_find_proc_info.c", + "libunwind/src/ptrace/_UPT_get_dyn_info_list_addr.c", + "libunwind/src/ptrace/_UPT_get_proc_name.c", + "libunwind/src/ptrace/_UPT_put_unwind_info.c", + "libunwind/src/ptrace/_UPT_reg_offset.c", + "libunwind/src/ptrace/_UPT_resume.c", ] libunwind_la_SOURCES_local_nounwind = [ - "src/mi/backtrace.c", - "src/mi/dyn-cancel.c", - "src/mi/dyn-info-list.c", - "src/mi/dyn-register.c", - "src/mi/Ldyn-extract.c", - "src/mi/Lfind_dynamic_proc_info.c", - "src/mi/Lget_proc_info_by_ip.c", - "src/mi/Lget_proc_name.c", - "src/mi/Lput_dynamic_unwind_info.c", - "src/mi/Ldestroy_addr_space.c", - "src/mi/Lget_reg.c", - "src/mi/Lset_reg.c", - "src/mi/Lget_fpreg.c", - "src/mi/Lset_fpreg.c", - "src/mi/Lset_caching_policy.c", - "src/mi/Lset_cache_size.c", + "libunwind/src/mi/backtrace.c", + "libunwind/src/mi/dyn-cancel.c", + "libunwind/src/mi/dyn-info-list.c", + "libunwind/src/mi/dyn-register.c", + "libunwind/src/mi/Ldyn-extract.c", + "libunwind/src/mi/Lfind_dynamic_proc_info.c", + "libunwind/src/mi/Lget_proc_info_by_ip.c", + "libunwind/src/mi/Lget_proc_name.c", + "libunwind/src/mi/Lput_dynamic_unwind_info.c", + "libunwind/src/mi/Ldestroy_addr_space.c", + "libunwind/src/mi/Lget_reg.c", + "libunwind/src/mi/Lset_reg.c", + "libunwind/src/mi/Lget_fpreg.c", + "libunwind/src/mi/Lset_fpreg.c", + "libunwind/src/mi/Lset_caching_policy.c", + "libunwind/src/mi/Lset_cache_size.c", ] libunwind_dwarf_local_la_SOURCES = [ - "src/dwarf/Lexpr.c", - "src/dwarf/Lfde.c", - "src/dwarf/Lparser.c", - "src/dwarf/Lpe.c", - "src/dwarf/Lfind_proc_info-lsb.c", - "src/dwarf/Lfind_unwind_table.c", + "libunwind/src/dwarf/Lexpr.c", + "libunwind/src/dwarf/Lfde.c", + "libunwind/src/dwarf/Lparser.c", + "libunwind/src/dwarf/Lpe.c", + "libunwind/src/dwarf/Lfind_proc_info-lsb.c", + "libunwind/src/dwarf/Lfind_unwind_table.c", ] # remove local file @@ -240,13 +236,13 @@ remove_sources += libunwind_dwarf_local_la_SOURCES remove_sources += ptrace_sources if (is_mingw) { - common_source += [ "src/mingw/pal-single-threaded.c" ] + common_source += [ "libunwind/src/mingw/pal-single-threaded.c" ] } config("unwind_config_public") { include_dirs = [ - "src", - "include", + "libunwind/src", + "libunwind/include", ] cflags = [ @@ -261,6 +257,7 @@ config("unwind_config_public") { "-Wno-unused-variable", "-Wno-unused-result", "-Wno-tautological-constant-out-of-range-compare", + "-Wno-bitfield-constant-conversion", ] if (use_wasm) { cflags += [ "-Wno-incompatible-pointer-types" ] @@ -278,7 +275,7 @@ config("unwind_config_public") { } if (target_cpu == "arm") { - include_dirs += [ "include/tdep-arm" ] + include_dirs += [ "libunwind/include/tdep-arm" ] cflags += [ "-Wno-inline-asm", "-Wno-shift-count-overflow", @@ -286,10 +283,10 @@ config("unwind_config_public") { "-Wno-unused-function", ] } else if (target_cpu == "arm64") { - include_dirs += [ "include/tdep-aarch64" ] + include_dirs += [ "libunwind/include/tdep-aarch64" ] cflags += [ "-Wno-incompatible-pointer-types" ] } else if (target_cpu == "x64") { - include_dirs += [ "include/tdep-x86_64" ] + include_dirs += [ "libunwind/include/tdep-x86_64" ] } } config("unwind_config_remote") { @@ -354,8 +351,8 @@ if (target_cpu == "arm") { # no jump lib arm_source -= [ - "src/arm/getcontext.S", - "src/arm/siglongjmp.S", + "libunwind/src/arm/getcontext.S", + "libunwind/src/arm/siglongjmp.S", ] sources += arm_source sources -= remove_sources @@ -372,8 +369,7 @@ if (target_cpu == "arm64") { ":unwind_config_arm64", ] sources = common_source - - arm64_source -= [ "src/aarch64/getcontext.S" ] + arm64_source -= [ "libunwind/src/aarch64/getcontext.S" ] sources += arm64_source sources -= remove_sources @@ -393,8 +389,8 @@ if (target_cpu == "x86") { # no jump lib x64_source -= [ - "src/x86_64/getcontext.S", - "src/x86_64/setcontext.S", + "libunwind/src/x86_64/getcontext.S", + "libunwind/src/x86_64/setcontext.S", ] sources += x64_source sources -= remove_sources @@ -417,18 +413,9 @@ ohos_source_set("unwind_source") { sources += x64_source public_configs = [ ":unwind_config_x64" ] } - - sources += [ "src/os-ohos.c" ] } -if (defined(ohos_lite)) { - source_set("libunwind") { - deps = [ ":unwind_source" ] - public_configs = [ ":unwind_config_public" ] - } -} else { - source_set("libunwind") { - deps = [ ":unwind_source" ] - public_configs = [ ":unwind_config_public" ] - } +source_set("libunwind") { + deps = [ ":unwind_source" ] + public_configs = [ ":unwind_config_public" ] } diff --git a/trace_streamer/sdk/demo_sdk/BUILD.gn b/trace_streamer/sdk/demo_sdk/BUILD.gn index 7278cc971..9c4e5bd00 100644 --- a/trace_streamer/sdk/demo_sdk/BUILD.gn +++ b/trace_streamer/sdk/demo_sdk/BUILD.gn @@ -107,12 +107,6 @@ ohos_source_set("trace_streamer_sdk") { "${THIRD_PARTY}/json/single_include/nlohmann", "${THIRD_PARTY}/bounds_checking_function/include", ] - if (!use_wasm) { - include_dirs += [ - "${THIRD_PARTY}/libunwind/include", - "${THIRD_PARTY}/libunwind/src", - ] - } deps = [ "${SRC}/base:base", "${THIRD_PARTY}/bounds_checking_function:libsec_static", diff --git a/trace_streamer/sdk/demo_sdk/test/BUILD.gn b/trace_streamer/sdk/demo_sdk/test/BUILD.gn index 06192a8e5..d92e58419 100644 --- a/trace_streamer/sdk/demo_sdk/test/BUILD.gn +++ b/trace_streamer/sdk/demo_sdk/test/BUILD.gn @@ -27,7 +27,7 @@ if (target == "sdkdemotest") { "../:trace_streamer_sdk", ] include_dirs = [ - "../base", + "${SRC}/base", "../sdk", "..", "../trace_streamer", diff --git a/trace_streamer/sdk/demo_sdk/test/unittest/sdk_api_test.cpp b/trace_streamer/sdk/demo_sdk/test/unittest/sdk_api_test.cpp index f94e8fe85..b87aa683a 100644 --- a/trace_streamer/sdk/demo_sdk/test/unittest/sdk_api_test.cpp +++ b/trace_streamer/sdk/demo_sdk/test/unittest/sdk_api_test.cpp @@ -35,7 +35,7 @@ public: public: TraceStreamerSelector stream_ = {}; - RpcServer *rpcServer = new RpcServer(); + DemoRpcServer *rpcServer = new DemoRpcServer(); }; std::string g_resultTest; @@ -62,25 +62,25 @@ HWTEST_F(SDKApiTest, SetTableName, TestSize.Level1) SetRpcServer(rpcServer); auto ret = SDKSetTableName("first_table", "second_table", "third_table", "fouth_table"); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from first_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_TRUE(ret); EXPECT_EQ(g_resultTest.find("ok"), 0); std::string sqlQueryCounterObj("select * from second_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); std::string sqlQuerySlice("select * from third_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); std::string sqlQuerySliceObj("select * from fouth_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -94,24 +94,24 @@ HWTEST_F(SDKApiTest, DefaultTableName, TestSize.Level1) { TS_LOGI("test1-2"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from counter_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); std::string sqlQueryCounterObj("select * from gpu_counter_object;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); std::string sqlQuerySlice("select * from slice_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); std::string sqlQuerySliceObj("select * from slice_object_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -127,24 +127,24 @@ HWTEST_F(SDKApiTest, NullTableName, TestSize.Level1) SetRpcServer(rpcServer); auto ret = SDKSetTableName(" ", " ", " ", " "); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from counter_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); std::string sqlQueryCounterObj("select * from gpu_counter_object;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); std::string sqlQuerySlice("select * from slice_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); std::string sqlQuerySliceObj("select * from slice_object_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); } @@ -160,24 +160,24 @@ HWTEST_F(SDKApiTest, NullAndManuallyCounterTableName, TestSize.Level1) SetRpcServer(rpcServer); auto ret = SDKSetTableName("first_table", "second_table", " ", " "); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from first_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); std::string sqlQueryCounterObj("select * from second_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); std::string sqlQuerySlice("select * from slice_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); std::string sqlQuerySliceObj("select * from slice_object_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); } @@ -193,24 +193,24 @@ HWTEST_F(SDKApiTest, NullAndManuallySliceTableName, TestSize.Level1) SetRpcServer(rpcServer); auto ret = SDKSetTableName(" ", " ", "first_table", "second_table"); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from counter_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); std::string sqlQueryCounterObj("select * from gpu_counter_object;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); std::string sqlQuerySlice("select * from first_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); std::string sqlQuerySliceObj("select * from second_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -224,14 +224,14 @@ HWTEST_F(SDKApiTest, CurrentDataForCounterObjectWithDefaultTableName, TestSize.L { TS_LOGI("test1-6"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounterObject(1, "counter_1"); EXPECT_EQ(0, ret); std::string sqlQueryCounterObj("select * from gpu_counter_object;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -245,15 +245,15 @@ HWTEST_F(SDKApiTest, CurrentDataForCounterObjectWithManuallyTableName, TestSize. { TS_LOGI("test1-7"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName(" ", "second_table", " ", " "); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName(" ", "second_table", " ", " "); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounterObject(1, "counter_1"); EXPECT_EQ(0, ret); std::string sqlQueryCounterObj("select * from second_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -267,14 +267,14 @@ HWTEST_F(SDKApiTest, WrongDataForCounterObjectWithDefaultTableName, TestSize.Lev { TS_LOGI("test1-8"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounterObject(INVALID_INT32, "counter_1"); EXPECT_EQ(0, ret); std::string sqlQueryCounterObj("select * from gpu_counter_object;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -288,15 +288,15 @@ HWTEST_F(SDKApiTest, WrongDataForCounterObjectWithManuallyTableName, TestSize.Le { TS_LOGI("test1-9"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName(" ", "second_table", " ", " "); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName(" ", "second_table", " ", " "); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounterObject(INVALID_INT32, "counter_1"); EXPECT_EQ(0, ret); std::string sqlQueryCounterObj("select * from second_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -310,15 +310,15 @@ HWTEST_F(SDKApiTest, WrongDataForCounterObject, TestSize.Level1) { TS_LOGI("test1-10"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName(" ", "second_table", " ", " "); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName(" ", "second_table", " ", " "); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounterObject(INVALID_INT32, " "); EXPECT_EQ(0, ret); std::string sqlQueryCounterObj("select * from gpu_counter_object;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounterObj.c_str(), sqlQueryCounterObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); } @@ -332,14 +332,14 @@ HWTEST_F(SDKApiTest, CurrentDataForCounterWithDefaultTableName, TestSize.Level1) { TS_LOGI("test1-11"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounter(1, 100, 100); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from counter_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -353,15 +353,15 @@ HWTEST_F(SDKApiTest, CurrentDataForCounterWithManuallyTableName, TestSize.Level1 { TS_LOGI("test1-12"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName("first_table", " ", " ", " "); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName("first_table", " ", " ", " "); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounter(1, 100, 100); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from first_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -375,14 +375,14 @@ HWTEST_F(SDKApiTest, WrongDataForCounterWithDefaultTableName, TestSize.Level1) { TS_LOGI("test1-13"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounter(INVALID_INT32, 100, 100); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from counter_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -396,15 +396,15 @@ HWTEST_F(SDKApiTest, WrongDataForCounterWithManuallyTableName, TestSize.Level1) { TS_LOGI("test1-14"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName("first_table", " ", " ", " "); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName("first_table", " ", " ", " "); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounter(INVALID_INT32, 100, 100); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from first_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -418,14 +418,14 @@ HWTEST_F(SDKApiTest, CounterWithWrongData, TestSize.Level1) { TS_LOGI("test1-15"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendCounter(INVALID_INT32, INVALID_UINT64, INVALID_INT32); EXPECT_EQ(0, ret); std::string sqlQueryCounter("select * from counter_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQueryCounter.c_str(), sqlQueryCounter.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -439,14 +439,14 @@ HWTEST_F(SDKApiTest, CurrentDataForSliceObjectWithDefaultTableName, TestSize.Lev { TS_LOGI("test1-16"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSliceObject(1, "slice_1"); EXPECT_EQ(0, ret); std::string sqlQuerySliceObj("select * from slice_object_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -460,15 +460,15 @@ HWTEST_F(SDKApiTest, CurrentDataForSliceObjectWithManuallyTableName, TestSize.Le { TS_LOGI("test1-17"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName(" ", " ", " ", "fourth_table"); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName(" ", " ", " ", "fourth_table"); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSliceObject(1, "slice_1"); EXPECT_EQ(0, ret); std::string sqlQuerySliceObj("select * from fourth_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -482,14 +482,14 @@ HWTEST_F(SDKApiTest, WrongDataForSliceObjectWithDefaultTableName, TestSize.Level { TS_LOGI("test1-18"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSliceObject(1, "slice_1"); EXPECT_EQ(0, ret); std::string sqlQuerySliceObj("select * from slice_object_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -503,15 +503,15 @@ HWTEST_F(SDKApiTest, WrongDataForSliceObjectWithManuallyTableName, TestSize.Leve { TS_LOGI("test1-19"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName(" ", " ", " ", "fourth_table"); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName(" ", " ", " ", "fourth_table"); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSliceObject(INVALID_INT32, "slice_1"); EXPECT_EQ(0, ret); std::string sqlQuerySliceObj("select * from slice_object_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); } @@ -525,15 +525,15 @@ HWTEST_F(SDKApiTest, WrongDataForSliceObject, TestSize.Level1) { TS_LOGI("test1-20"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName(" ", " ", " ", "fourth_table"); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName(" ", " ", " ", "fourth_table"); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSliceObject(INVALID_INT32, " "); EXPECT_EQ(0, ret); std::string sqlQuerySliceObj("select * from slice_object_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySliceObj.c_str(), sqlQuerySliceObj.length(), res); EXPECT_EQ(g_resultTest.find("ok"), string::npos); EXPECT_FALSE(ret); } @@ -547,14 +547,14 @@ HWTEST_F(SDKApiTest, CurrentDataForSliceWithDefaultTableName, TestSize.Level1) { TS_LOGI("test1-21"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSlice(1, 100, 100, 100); EXPECT_EQ(0, ret); std::string sqlQuerySlice("select * from Slice_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -568,15 +568,15 @@ HWTEST_F(SDKApiTest, CurrentDataForSliceWithManuallyTableName, TestSize.Level1) { TS_LOGI("test1-22"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName(" ", " ", "third_table", " "); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName(" ", " ", "third_table", " "); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSlice(1, 100, 100, 100); EXPECT_EQ(0, ret); std::string sqlQuerySlice("select * from third_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -590,14 +590,14 @@ HWTEST_F(SDKApiTest, WrongDataForSliceWithDefaultTableName, TestSize.Level1) { TS_LOGI("test1-23"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSlice(INVALID_INT32, 100, 100, 100); EXPECT_EQ(0, ret); std::string sqlQuerySlice("select * from Slice_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -611,15 +611,15 @@ HWTEST_F(SDKApiTest, WrongDataForSliceWithManuallyTableName, TestSize.Level1) { TS_LOGI("test1-24"); SetRpcServer(rpcServer); - auto ret = SDKSetTableName(" ", " ", "third_table", " "); - ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = SDK_SetTableName(" ", " ", "third_table", " "); + ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSlice(INVALID_INT32, 100, 100, 100); EXPECT_EQ(0, ret); std::string sqlQuerySlice("select * from third_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } @@ -633,14 +633,14 @@ HWTEST_F(SDKApiTest, SliceWithWrongData, TestSize.Level1) { TS_LOGI("test1-25"); SetRpcServer(rpcServer); - auto ret = rpcServer->ts_->sdkDataParser_->GetJsonConfig(QueryResultCallback); + auto ret = rpcServer->demoTs_->sdkDataParser_->GetJsonConfig(QueryResultCallback); EXPECT_EQ(0, ret); - ret = rpcServer->ts_->sdkDataParser_->CreateTableByJson(); + ret = rpcServer->demoTs_->sdkDataParser_->CreateTableByJson(); EXPECT_EQ(0, ret); ret = SDKAppendSlice(INVALID_INT32, INVALID_UINT64, INVALID_UINT64, INVALID_INT32); EXPECT_EQ(0, ret); std::string sqlQuerySlice("select * from slice_table;"); - ret = rpcServer->SqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); + ret = rpcServer->DemoSqlQuery((const uint8_t *)sqlQuerySlice.c_str(), sqlQuerySlice.length(), res); EXPECT_EQ(g_resultTest.find("ok"), 0); EXPECT_TRUE(ret); } diff --git a/trace_streamer/sdk/demo_sdk/ts.gni b/trace_streamer/sdk/demo_sdk/ts.gni index 4ca0cd25e..4c69fad04 100644 --- a/trace_streamer/sdk/demo_sdk/ts.gni +++ b/trace_streamer/sdk/demo_sdk/ts.gni @@ -11,7 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. OHOS_PROTO_DIR = "" - +THIRD_PARTY = "//third_party" +SRC = "//src" if (target_os == "linux" || target_os == "macx" || target_os == "windows") { OHOS_FTRACE_PROTO_DIR = "//src/multi_platform" OHOS_MEMORY_PROTO_DIR = "//src/multi_platform" diff --git a/trace_streamer/src/BUILD.gn b/trace_streamer/src/BUILD.gn index 455d78792..c63d5a454 100644 --- a/trace_streamer/src/BUILD.gn +++ b/trace_streamer/src/BUILD.gn @@ -80,9 +80,6 @@ config("trace_streamer_cfg") { "${THIRD_PARTY}/elfutils/libelf", ] } - if (is_mingw) { - cflags = [ "-includeMingW64Fix.h" ] - } } ohos_source_set("trace_streamer_source") { @@ -112,7 +109,7 @@ ohos_source_set("trace_streamer_source") { "table:table", "trace_data:trace_data", "//third_party/bounds_checking_function:libsec_static", - "//third_party/perf_include/hiviewdfx:hiviewdfx", + "//third_party/perf_include/hiviewdfx:libfaultloggerd", ] public_configs = [ ":trace_streamer_cfg" ] public_deps = [] diff --git a/trace_streamer/src/base/file.cpp b/trace_streamer/src/base/file.cpp index 338506437..fe4c4ff13 100644 --- a/trace_streamer/src/base/file.cpp +++ b/trace_streamer/src/base/file.cpp @@ -75,5 +75,23 @@ std::string GetExecutionDirectoryPath() std::string str(currPath); return str.substr(0, str.find_last_of('/')); } +#ifdef is_linux +std::vector GetFilesNameFromDir(const std::string &path) +{ + std::vector soFiles; + + std::filesystem::path dirPath(path); + // 检查文件是否存在 + if (!std::filesystem::exists(dirPath)) { + std::cout << "!std::filesystem::exists(dirPath)" << std::endl; + return soFiles; + } + // 遍历目录 + for (const auto &entry : std::filesystem::directory_iterator(dirPath)) { + soFiles.emplace_back(entry.path().string()); + } + return soFiles; +} +#endif } // namespace base } // namespace SysTuning diff --git a/trace_streamer/src/base/file.h b/trace_streamer/src/base/file.h index 04c5d01b3..931fc18e4 100644 --- a/trace_streamer/src/base/file.h +++ b/trace_streamer/src/base/file.h @@ -16,8 +16,11 @@ #ifndef INCLUDE_TUNING_BASE_FILE_UTILS_H #define INCLUDE_TUNING_BASE_FILE_UTILS_H +#ifdef is_linux +#include +#endif #include - +#include namespace SysTuning { namespace base { #define TS_PERMISSION_RW 0600 @@ -39,6 +42,9 @@ ssize_t Read(int32_t fd, uint8_t *dst, size_t dstSize); int32_t OpenFile(const std::string &path, int32_t flags, uint32_t mode = K_FILE_MODE_INVALID); std::string GetExecutionDirectoryPath(); +#ifdef is_linux +std::vector GetFilesNameFromDir(const std::string &fileDir); +#endif } // namespace base } // namespace SysTuning #endif // INCLUDE_TUNING_BASE_FILE_UTILS_H_ diff --git a/trace_streamer/src/cfg/trace_streamer_config.cpp b/trace_streamer/src/cfg/trace_streamer_config.cpp index ef3ba27e1..862ff0d3d 100644 --- a/trace_streamer/src/cfg/trace_streamer_config.cpp +++ b/trace_streamer/src/cfg/trace_streamer_config.cpp @@ -142,6 +142,11 @@ inline void TraceStreamerConfig::InitInterruptEventNameMap() eventNameMap_.emplace(TRACE_EVENT_SOFTIRQ_RAISE, TRACE_ACTION_SOFTIRQ_RAISE); eventNameMap_.emplace(TRACE_EVENT_SOFTIRQ_ENTRY, TRACE_ACTION_SOFTIRQ_ENTRY); eventNameMap_.emplace(TRACE_EVENT_SOFTIRQ_EXIT, TRACE_ACTION_SOFTIRQ_EXIT); + eventNameMap_.emplace(TRACE_EVENT_DMA_FENCE_INIT, TRACE_ACTION_DMA_FENCE_INIT); + eventNameMap_.emplace(TRACE_EVENT_DMA_FENCE_DESTROY, TRACE_ACTION_DMA_FENCE_DESTROY); + eventNameMap_.emplace(TRACE_EVENT_DMA_FENCE_ENABLE, TRACE_ACTION_DMA_FENCE_ENABLE); + eventNameMap_.emplace(TRACE_EVENT_DMA_FENCE_SIGNALED, TRACE_ACTION_DMA_FENCE_SIGNALED); + eventNameMap_.emplace(TRACE_EVENT_DMA_FENCE, TRACE_ACTION_DMA_FENCE); } inline void TraceStreamerConfig::InitMemoryEventNameMap() { @@ -534,6 +539,11 @@ inline void TraceStreamerConfig::InitInterruptEventSecurityMap() eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SOFTIRQ_RAISE, statSeverityDescMap_); eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SOFTIRQ_ENTRY, statSeverityDescMap_); eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SOFTIRQ_EXIT, statSeverityDescMap_); + eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE_INIT, statSeverityDescMap_); + eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE_DESTROY, statSeverityDescMap_); + eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE_ENABLE, statSeverityDescMap_); + eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE_SIGNALED, statSeverityDescMap_); + eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE, statSeverityDescMap_); } inline void TraceStreamerConfig::InitMemoryEventSecurityMap() { diff --git a/trace_streamer/src/cfg/trace_streamer_config.h b/trace_streamer/src/cfg/trace_streamer_config.h index f1b065d68..12db21c82 100644 --- a/trace_streamer/src/cfg/trace_streamer_config.h +++ b/trace_streamer/src/cfg/trace_streamer_config.h @@ -116,6 +116,11 @@ enum SupportedTraceEventType { TRACE_GPU_PROCESS_MEM, TRACE_GPU_WINDOW_MEM, TRACE_WINDOW_MANAGER_SERVICE, + TRACE_EVENT_DMA_FENCE_INIT, + TRACE_EVENT_DMA_FENCE_DESTROY, + TRACE_EVENT_DMA_FENCE_ENABLE, + TRACE_EVENT_DMA_FENCE_SIGNALED, + TRACE_EVENT_DMA_FENCE, TRACE_EVENT_OTHER, TRACE_EVENT_MAX }; @@ -306,6 +311,11 @@ private: const std::string TRACE_ACTION_BLOCK_RQ_INSERT = "trace_block_rq_insert"; const std::string TRACE_ACTION_BLOCK_RQ_REMAP = "trace_block_rq_remap"; const std::string TRACE_ACTION_BLOCK_RQ_ISSUE = "trace_block_rq_issue"; + const std::string TRACE_ACTION_DMA_FENCE_INIT = "dma_fence_init"; + const std::string TRACE_ACTION_DMA_FENCE_DESTROY = "dma_fence_destroy"; + const std::string TRACE_ACTION_DMA_FENCE_ENABLE = "dma_fence_enable_signal"; + const std::string TRACE_ACTION_DMA_FENCE_SIGNALED = "dma_fence_signaled"; + const std::string TRACE_ACTION_DMA_FENCE = "dma_fence"; const std::string TRACE_ACTION_FFRT = "trace_ffrt"; const std::string TRACE_ACTION_SYS_MEMORY = "sys_memory"; diff --git a/trace_streamer/src/filter/binder_filter.cpp b/trace_streamer/src/filter/binder_filter.cpp index 03c19a12e..63f674453 100644 --- a/trace_streamer/src/filter/binder_filter.cpp +++ b/trace_streamer/src/filter/binder_filter.cpp @@ -88,14 +88,21 @@ void BinderFilter::SendTraction(int64_t ts, } // the flowing code should be under the ubove conditions, but this will bring a big impact to the UI-SHOW (void)streamFilters_->sliceFilter_->EndBinder(ts, tid, INVALID_UINT64, INVALID_UINT64, argsSend); - transReplyWaitingReply_.insert(transactionId); + transReplyDest_[transactionId] = destTid; return; } else { bool needReply = !isReply && !(flags & noReturnMsgFlag_); if (needReply) { // transaction needs reply TAG-1 (void)streamFilters_->sliceFilter_->BeginBinder(ts, tid, binderCatalogId_, transSliceId_, argsSend); - transNeedReply_[transactionId] = tid; + if (destTid != 0) { + // 如果transaction携带了destTid,保存在transReplyFilter_中 + transReplyFilter_[destTid] = tid; + transactionInfo_[transactionId] = std::make_pair(tid, destTid); + } else { + // 没有携带destTid,以receiver的线程作为destTid + transactionInfo_[transactionId] = std::make_pair(tid, INVALID_UINT32); + } } else { // transaction do not need reply // tid calling id @@ -107,40 +114,54 @@ void BinderFilter::SendTraction(int64_t ts, } void BinderFilter::ReceiveTraction(int64_t ts, uint32_t pid, uint64_t transactionId) { - InternalTid internalTid = streamFilters_->processFilter_->UpdateOrCreateThread(ts, pid); - const auto threadName = traceDataCache_->GetConstThreadData(internalTid).nameIndex_; - if (transReplyWaitingReply_.count(transactionId)) { - (void)streamFilters_->sliceFilter_->EndBinder(ts, pid); - transReplyWaitingReply_.erase(transactionId); + if (transReplyDest_.count(transactionId)) { + (void)streamFilters_->sliceFilter_->EndBinder(ts, transReplyDest_[transactionId]); + transReplyDest_.erase(transactionId); return; } - if (transNeedReply_.count(transactionId)) { + if (transactionInfo_.count(transactionId)) { + uint32_t replyTid; + if (transactionInfo_[transactionId].second == INVALID_UINT32) { + // binder transaction 没有携带destId, 取当前binder transaction received的线程id为transaction destTid + replyTid = pid; + transReplyFilter_[pid] = transactionInfo_[transactionId].first; + } else { + replyTid = transactionInfo_[transactionId].second; + } // First, begin the reply, the reply will be end in "SendTraction" func, and the isReply will be true, TAG-2 - auto replySliceid = streamFilters_->sliceFilter_->BeginBinder(ts, pid, binderCatalogId_, replyId_); - transReplyFilter_[pid] = transNeedReply_[transactionId]; + auto replySliceid = streamFilters_->sliceFilter_->BeginBinder(ts, replyTid, binderCatalogId_, replyId_); + // Add dest info to the reply + InternalTid internalTid = streamFilters_->processFilter_->UpdateOrCreateThread(ts, replyTid); + const auto threadName = traceDataCache_->GetConstThreadData(internalTid).nameIndex_; ArgsSet args; - args.AppendArg(destThreadId_, BASE_DATA_TYPE_INT, pid); + args.AppendArg(destThreadId_, BASE_DATA_TYPE_INT, replyTid); args.AppendArg(destThreadNameId_, BASE_DATA_TYPE_STRING, threadName); if (IsValidUint32(static_cast(replySliceid))) { args.AppendArg(destSliceId_, BASE_DATA_TYPE_INT, replySliceid); + } else { + TS_LOGD("ReceiveTraction, replySliceid value is INVALID!"); + return; } // Add dest args - uint64_t transSliceId = INVALID_UINT64; + uint64_t transSliceId = INVALID_UINT32; uint32_t argSetId = INVALID_UINT32; - std::tie(transSliceId, argSetId) = streamFilters_->sliceFilter_->AddArgs(transNeedReply_[transactionId], + std::tie(transSliceId, argSetId) = streamFilters_->sliceFilter_->AddArgs(transactionInfo_[transactionId].first, binderCatalogId_, transSliceId_, args); // remeber dest slice-id to the argset from "SendTraction" TAG-1 ArgsSet replyDestInserter; if (IsValidUint32(transSliceId)) { replyDestInserter.AppendArg(destSliceId_, BASE_DATA_TYPE_INT, transSliceId); + } else { + TS_LOGD("ReceiveTraction, transSliceId value is INVALID!"); + return; } std::tie(transSliceId, argSetId) = - streamFilters_->sliceFilter_->AddArgs(pid, binderCatalogId_, replyId_, replyDestInserter); + streamFilters_->sliceFilter_->AddArgs(replyTid, binderCatalogId_, replyId_, replyDestInserter); traceDataCache_->GetInternalSlicesData()->SetArgSetId(transSliceId, argSetId); - transNeedReply_.erase(transactionId); + transactionInfo_.erase(transactionId); return; } // the code below can be hard to understand, may be a EndBinder will be better @@ -197,8 +218,6 @@ bool BinderFilter::IsAsync(int32_t flags) const void BinderFilter::Clear() { lastEventTs_.clear(); - transReplyWaitingReply_.clear(); - transNeedReply_.clear(); asyncBinderEvents_.clear(); } } // namespace TraceStreamer diff --git a/trace_streamer/src/filter/binder_filter.h b/trace_streamer/src/filter/binder_filter.h index 30dcca0a9..d4eb5a04d 100644 --- a/trace_streamer/src/filter/binder_filter.h +++ b/trace_streamer/src/filter/binder_filter.h @@ -80,8 +80,9 @@ private: const DataIndex dataOffsetSizeId_ = traceDataCache_->GetDataIndex("offsets size"); const DataIndex nullStringId_ = traceDataCache_->GetDataIndex("null"); std::unordered_map lastEventTs_ = {}; - std::unordered_set transReplyWaitingReply_ = {}; - std::unordered_map transNeedReply_ = {}; + std::unordered_map transReplyDest_ = {}; + std::unordered_map> transactionInfo_ = + {}; // 记录每一个transaction的src_tid和dest_tid std::unordered_map transReplyFilter_ = {}; std::unordered_map asyncBinderEvents_ = {}; std::unordered_map binderFlagDescs_ = {}; diff --git a/trace_streamer/src/filter/hook_filter/BUILD.gn b/trace_streamer/src/filter/hook_filter/BUILD.gn index f3b05bc70..8af327660 100644 --- a/trace_streamer/src/filter/hook_filter/BUILD.gn +++ b/trace_streamer/src/filter/hook_filter/BUILD.gn @@ -15,7 +15,10 @@ import("//build/ohos.gni") import("../../../build/ts.gni") config("native_hook_filter_cfg") { - include_dirs = [ "." ] + include_dirs = [ + ".", + "${COMMON_LIBRARY}/base/include", + ] } ohos_static_library("native_hook_filter") { diff --git a/trace_streamer/src/filter/hook_filter/native_hook_filter.cpp b/trace_streamer/src/filter/hook_filter/native_hook_filter.cpp index 237a95a25..88d46e1e8 100644 --- a/trace_streamer/src/filter/hook_filter/native_hook_filter.cpp +++ b/trace_streamer/src/filter/hook_filter/native_hook_filter.cpp @@ -1196,7 +1196,7 @@ void NativeHookFilter::UpdateFilePathIdAndStValueToSymAddrMap(T *firstSymbolAddr } } -bool NativeHookFilter::NativeHookReloadElfSymbolTable(const std::vector> &symbolsFiles) +void NativeHookFilter::NativeHookReloadElfSymbolTable(const std::vector> &symbolsFiles) { auto nativeHookFrame = traceDataCache_->GetNativeHookFrameData(); auto size = nativeHookFrame->Size(); @@ -1227,7 +1227,6 @@ bool NativeHookFilter::NativeHookReloadElfSymbolTable(const std::vector &nativeHookMetaData); void ParseTagEvent(const ProtoReader::BytesView &bytesView); void FinishParseNativeHookData(); - bool NativeHookReloadElfSymbolTable(const std::vector> &symbolsFiles); + void NativeHookReloadElfSymbolTable(const std::vector> &symbolsFiles); CommHookData &GetCommHookData(); ProfilerPluginData *GetHookPluginData(); void SerializeHookCommDataToString(); diff --git a/trace_streamer/src/filter/hook_filter/offline_symbolization_filter.cpp b/trace_streamer/src/filter/hook_filter/offline_symbolization_filter.cpp index a2fe90cab..a7431a177 100644 --- a/trace_streamer/src/filter/hook_filter/offline_symbolization_filter.cpp +++ b/trace_streamer/src/filter/hook_filter/offline_symbolization_filter.cpp @@ -152,40 +152,5 @@ std::shared_ptr OfflineSymbolizationFilter::OfflineSymbolizationByIp( ipidToIpToFrameInfo_.Insert(ipid, ip, frameInfo); return frameInfo; } -DataIndex OfflineSymbolizationFilter::OfflineSymbolizationByVaddr(uint64_t symVaddr, DataIndex filePathIndex) -{ - auto &symbolTable = filePathIdToImportSymbolTableMap_.at(filePathIndex); - // pase sym_table to Elf32_Sym or Elf64_Sym array decided by sym_entry_size. - auto symEntLen = symbolTable->symEntSize; - auto startValueToSymAddrMap = filePathIdAndStValueToSymAddr_.Find(filePathIndex); - if (!startValueToSymAddrMap) { - return INVALID_DATAINDEX; - } - // Traverse array, st_value <= symVaddr and symVaddr <= st_value + st_size. then you can get st_name - auto end = startValueToSymAddrMap->upper_bound(symVaddr); - auto length = std::distance(startValueToSymAddrMap->begin(), end); - uint32_t symbolStart = INVALID_UINT32; - if (length > 0) { - end--; - if (symEntLen == ELF32_SYM) { - GetSymbolStartMaybeUpdateFrameInfo(reinterpret_cast(end->second), symbolStart, symVaddr, - 0, nullptr); - } else { - GetSymbolStartMaybeUpdateFrameInfo(reinterpret_cast(end->second), symbolStart, symVaddr, - 0, nullptr); - } - } - if (symbolStart == INVALID_UINT32 || symbolStart >= symbolTable->strTable.size()) { - TS_LOGD("symbolStart is : %u invaliable!!!", symbolStart); - return INVALID_DATAINDEX; - } - auto mangle = symbolTable->strTable.c_str() + symbolStart; - auto demangle = base::GetDemangleSymbolIndex(mangle); - auto index = traceDataCache_->GetDataIndex(demangle); - if (demangle != mangle) { - free(demangle); - } - return index; -} } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/src/filter/hook_filter/offline_symbolization_filter.h b/trace_streamer/src/filter/hook_filter/offline_symbolization_filter.h index f0083cc04..aa73e9ada 100644 --- a/trace_streamer/src/filter/hook_filter/offline_symbolization_filter.h +++ b/trace_streamer/src/filter/hook_filter/offline_symbolization_filter.h @@ -63,7 +63,6 @@ public: OfflineSymbolizationFilter(TraceDataCache *dataCache, const TraceStreamerFilters *filter); ~OfflineSymbolizationFilter() override = default; std::shared_ptr OfflineSymbolizationByIp(uint64_t ipid, uint64_t ip); - DataIndex OfflineSymbolizationByVaddr(uint64_t symVaddr, DataIndex filePathIndex); protected: enum SYSTEM_ENTRY_VALUE { ELF32_SYM = 16, ELF64_SYM = 24 }; diff --git a/trace_streamer/src/filter/slice_filter.cpp b/trace_streamer/src/filter/slice_filter.cpp index f5cdb9091..00ad01d3a 100644 --- a/trace_streamer/src/filter/slice_filter.cpp +++ b/trace_streamer/src/filter/slice_filter.cpp @@ -22,6 +22,7 @@ #include "measure_filter.h" #include "process_filter.h" #include "stat_filter.h" +#include "string_help.h" #include "string_to_numerical.h" #include "ts_common.h" @@ -161,6 +162,17 @@ void SliceFilter::SoftIrqExit(uint64_t timeStamp, uint32_t cpu, ArgsSet args) return; } +void SliceFilter::DmaFence(DmaFenceRow &dmaFenceRow) +{ + if (dmaFenceEventMap_.find(dmaFenceRow.timeline) == dmaFenceEventMap_.end()) { + dmaFenceEventMap_.emplace(dmaFenceRow.timeline, dmaFenceRow.timeStamp); + } else { + dmaFenceRow.duration = dmaFenceRow.timeStamp - dmaFenceEventMap_.at(dmaFenceRow.timeline); + dmaFenceEventMap_.at(dmaFenceRow.timeline) = dmaFenceRow.timeStamp; + } + traceDataCache_->GetDmaFenceData()->AppendNew(dmaFenceRow); +} + void SliceFilter::RememberSliceData(InternalTid internalTid, std::unordered_map &stackMap, SliceData &slice, @@ -316,7 +328,7 @@ size_t SliceFilter::StartSlice(uint64_t timeStamp, CallStackInternalRow callStackInternalRow = {sliceData.timeStamp, static_cast(sliceData.duration), sliceData.internalTid, sliceData.cat, sliceData.name, 0}; - size_t index = slices->AppendInternalSlice(callStackInternalRow, std::nullopt); + size_t index = slices->AppendInternalSlice(callStackInternalRow, parentId); if (depth >= std::numeric_limits::max()) { return SIZE_MAX; } @@ -432,24 +444,29 @@ uint64_t SliceFilter::StartAsyncSlice(uint64_t timeStamp, int64_t cookie, DataIndex nameIndex) { - Unused(pid); InternalPid internalTid = streamFilters_->processFilter_->UpdateOrCreateThread(timeStamp, threadGroupId); - + uint32_t parentId = streamFilters_->processFilter_->UpdateOrCreateThread(timeStamp, pid); auto lastFilterId = asyncEventMap_.Find(internalTid, cookie, nameIndex); auto slices = traceDataCache_->GetInternalSlicesData(); + auto cat = INVALID_UINT64; if (lastFilterId != INVALID_UINT64) { asyncEventDisMatchCount_++; return INVALID_UINT64; } asyncEventSize_++; + std::smatch matchLine; + if (std::regex_match(traceDataCache_->GetDataFromDict(nameIndex), matchLine, categoryReg_)) { + std::string category = matchLine[categoryMatchedIdx_].str(); + cat = traceDataCache_->GetDataIndex(Strip(category)); + } // a pid, cookie and function name determain a callstack asyncEventMap_.Insert(internalTid, cookie, nameIndex, asyncEventSize_); // the IDE need a depth to paint call slice in different position of the canvas, the depth of async call // do not mean the parent-to-child relationship, it is different from no-async call uint8_t depth = 0; - CallStackInternalRow callStackInternalRow = { - timeStamp, static_cast(-1), internalTid, INVALID_UINT64, nameIndex, depth}; - size_t index = slices->AppendInternalAsyncSlice(callStackInternalRow, cookie, std::nullopt); + CallStackInternalRow callStackInternalRow = {timeStamp, static_cast(-1), internalTid, cat, nameIndex, + depth}; + size_t index = slices->AppendInternalAsyncSlice(callStackInternalRow, cookie, parentId); asyncEventFilterMap_.insert(std::make_pair(asyncEventSize_, AsyncEvent{timeStamp, index})); return index; } @@ -489,7 +506,6 @@ void SliceFilter::StartGEvent(uint64_t timeStamp, int64_t cookie, DataIndex nameIndex) { - Unused(pid); InternalPid internalTid = streamFilters_->processFilter_->UpdateOrCreateThread(timeStamp, threadGroupId); auto gEventRes = gEventMap_.Find(internalTid, cookie, nameIndex); auto slices = traceDataCache_->GetInternalSlicesData(); @@ -539,6 +555,7 @@ uint64_t SliceFilter::FinishHEvent(uint64_t timeStamp, uint32_t threadGroupId, i streamFilters_->processFilter_->AddThreadSliceNum(internalTid); return lastRow; } + size_t SliceFilter::EndSlice(uint64_t timeStamp, uint32_t pid, uint32_t threadGroupId, @@ -602,6 +619,8 @@ void SliceFilter::Clear() depthHolder_.clear(); sliceRowToArgsSetId_.clear(); argsSet_.clear(); + gEventMap_.Clear(); + gEventFilterMap_.clear(); } } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/src/filter/slice_filter.h b/trace_streamer/src/filter/slice_filter.h index 83d6b6f2d..2a46bc38e 100644 --- a/trace_streamer/src/filter/slice_filter.h +++ b/trace_streamer/src/filter/slice_filter.h @@ -93,6 +93,7 @@ public: void IpiHandlerExit(uint64_t timeStamp, uint32_t cpu); void SoftIrqEntry(uint64_t timeStamp, uint32_t cpu, DataIndex catalog, DataIndex nameIndex); void SoftIrqExit(uint64_t timeStamp, uint32_t cpu, ArgsSet args); + void DmaFence(DmaFenceRow &dmaFenceRow); void Clear(); void UpdateReadySize() { @@ -124,8 +125,8 @@ private: private: // The parameter list is tid, cookid, functionName, asyncCallId. TripleMap asyncEventMap_; - // this is only used to calc the layer of the async event in same time range TripleMap> gEventMap_; + // this is only used to calc the layer of the async event in same time range std::map asyncNoEndingEventMap_ = {}; // irq map, key1 is cpu, key2 struct IrqRecords { @@ -136,6 +137,7 @@ private: std::unordered_map ipiEventMap_ = {}; // irq map, key1 is cpu, key2 std::unordered_map softIrqEventMap_ = {}; + std::unordered_map dmaFenceEventMap_ = {}; std::map asyncEventFilterMap_ = {}; std::map gEventFilterMap_ = {}; std::unordered_map sliceStackMap_ = {}; @@ -157,6 +159,8 @@ private: DataIndex asyncBeginTsId_ = traceDataCache_->GetDataIndex("legacy_unnestable_last_begin_ts"); DataIndex ipiId_ = traceDataCache_->GetDataIndex("IPI"); std::map irqDataLinker_ = {}; + const std::regex categoryReg_ = std::regex(R"((.+)\$\$(.+))"); + const uint64_t categoryMatchedIdx_ = 1; }; } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/src/main.cpp b/trace_streamer/src/main.cpp index 215356377..e3708b970 100644 --- a/trace_streamer/src/main.cpp +++ b/trace_streamer/src/main.cpp @@ -362,6 +362,7 @@ struct TraceExportOption { bool closeMutiThread = false; uint8_t parserThreadNum = INVALID_UINT8; bool needClearLongTraceCache = true; + std::string soFilesDir; }; bool CheckFinal(char **argv, TraceExportOption &traceExportOption) { @@ -375,7 +376,6 @@ bool CheckFinal(char **argv, TraceExportOption &traceExportOption) } return true; } - bool CheckArgc(int argc, char **argv, int curArgNum) { if (curArgNum == argc) { @@ -384,6 +384,12 @@ bool CheckArgc(int argc, char **argv, int curArgNum) } return true; } +bool CheckAndSetSoFilesPath(TraceExportOption &traceExportOption, int argc, char **argv, int &index) +{ + TS_CHECK_TRUE_RET(CheckArgc(argc, argv, ++index), false); + traceExportOption.soFilesDir = std::string(argv[index]); + return true; +} bool CheckAndSetLogLevel(int argc, char **argv, int &index) { TS_CHECK_TRUE_RET(CheckArgc(argc, argv, ++index), false); @@ -530,6 +536,10 @@ bool ParseArgs(int argc, char **argv, TraceExportOption &traceExportOption) TS_CHECK_TRUE_RET(CheckAndSetOutputFilePath(traceExportOption, argc, argv, i), false); i++; continue; + } else if (!strcmp(argv[i], "--So_dir")) { + TS_CHECK_TRUE_RET(CheckAndSetSoFilesPath(traceExportOption, argc, argv, i), false); + i++; + continue; } else if (!ParseOtherArgs(argc, argv, traceExportOption, i)) { return false; } @@ -729,6 +739,12 @@ int main(int argc, char **argv) } return 1; } +#ifdef is_linux + if (!traceExportOption.soFilesDir.empty()) { + auto values = GetFilesNameFromDir(traceExportOption.soFilesDir); + ts.ReloadSymbolFiles(traceExportOption.soFilesDir, values); + } +#endif if (traceExportOption.interactiveState) { TS_CHECK_TRUE_RET(EnterInteractiveState(ts), 1); } diff --git a/trace_streamer/src/parser/BUILD.gn b/trace_streamer/src/parser/BUILD.gn index 421a22a3d..92f6fcd31 100644 --- a/trace_streamer/src/parser/BUILD.gn +++ b/trace_streamer/src/parser/BUILD.gn @@ -39,20 +39,6 @@ config("parser_base_cfg") { "${THIRD_PARTY}/json/single_include/nlohmann", "${THIRD_PARTY}/profiler/device/plugins/ftrace_plugin/include", ] - include_dirs += [ - "${PERF_DIR}/hiperf/include/nonlinux/linux", - "${PERF_DIR}/hiperf/include/nonlinux", - "${PERF_DIR}/hiperf/include", - "${THIRD_PARTY}/perf_include", - "${THIRD_PARTY}/perf_include/libbpf", - "${THIRD_PARTY}/perf_include/hiviewdfx/hilog/include", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/cutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxlog", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/common", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/nonlinux", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/include", - ] include_dirs += [ "${SRC}/trace_data/trace_stdtype", "${SRC}/trace_data/trace_stdtype/ftrace", diff --git a/trace_streamer/src/parser/ebpf_parser/BUILD.gn b/trace_streamer/src/parser/ebpf_parser/BUILD.gn index 9a1512a2a..224d47879 100644 --- a/trace_streamer/src/parser/ebpf_parser/BUILD.gn +++ b/trace_streamer/src/parser/ebpf_parser/BUILD.gn @@ -59,17 +59,6 @@ config("ebpf_parser_cfg") { cflags += [ "-D IS_UT" ] } } - include_dirs += [ - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/nonlinux", - "${THIRD_PARTY}/perf_include/linux", - "${THIRD_PARTY}/hiperf/include", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/include", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/common", - "${THIRD_PARTY}/hiperf/include/nonlinux/linux", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxutil", - "${THIRD_PARTY}/hiperf/include/nonlinux/", - "${THIRD_PARTY}/perf_include/libbpf", - ] } ohos_static_library("ebpf_parser") { @@ -87,7 +76,7 @@ ohos_static_library("ebpf_parser") { public_configs = [ ":ebpf_parser_cfg" ] public_deps = [ "${OHOS_TRACE_STREAMER_PROTOS_DIR}/protos/types/plugins/memory_data:memory_data_reader", - "${PERF_DIR}/hiperf:hiperf_platform_common", + "${PERF_DIR}/hiperf:hiperf_src", ] deps = [ "${THIRD_PARTY}/protobuf:protobuf_lite_static", diff --git a/trace_streamer/src/parser/ebpf_parser/bio_latency_data_parser.cpp b/trace_streamer/src/parser/ebpf_parser/bio_latency_data_parser.cpp index c5d1dcac8..5f58ff549 100644 --- a/trace_streamer/src/parser/ebpf_parser/bio_latency_data_parser.cpp +++ b/trace_streamer/src/parser/ebpf_parser/bio_latency_data_parser.cpp @@ -47,15 +47,12 @@ const uint64_t *BioLatencyDataParser::IPAndCallIdProcessing(const BIOFixedHeader currentCallId_ = callChainId_++; } } else { - currentCallId_ = INVALID_UINT64; + currentCallId_ = INVALID_UINT32; } return userIpsAddr; } void BioLatencyDataParser::ParseBioLatencyEvent() { - if (!reader_->GetBIOSampleMap().size()) { - return; - } for (auto mapItor = reader_->GetBIOSampleMap().begin(); mapItor != reader_->GetBIOSampleMap().end(); mapItor++) { streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_EBPF_BIO_LATENCY, STAT_EVENT_RECEIVED); auto bioFixedHeadrAddr = mapItor->second; diff --git a/trace_streamer/src/parser/ebpf_parser/ebpf_base.cpp b/trace_streamer/src/parser/ebpf_parser/ebpf_base.cpp index 9483f83b9..5fc741344 100644 --- a/trace_streamer/src/parser/ebpf_parser/ebpf_base.cpp +++ b/trace_streamer/src/parser/ebpf_parser/ebpf_base.cpp @@ -215,7 +215,7 @@ void EbpfBase::UpdateFilePathIndexAndStValueToSymAddrMap(T *firstSymbolAddr, con } } } -bool EbpfBase::EBPFReloadElfSymbolTable(const std::vector> &symbolsFiles) +void EbpfBase::EBPFReloadElfSymbolTable(const std::vector> &symbolsFiles) { auto ebpfCallStackDate = traceDataCache_->GetEbpfCallStack(); auto size = ebpfCallStackDate->Size(); @@ -241,7 +241,6 @@ bool EbpfBase::EBPFReloadElfSymbolTable(const std::vector diff --git a/trace_streamer/src/parser/ebpf_parser/ebpf_base.h b/trace_streamer/src/parser/ebpf_parser/ebpf_base.h index 7625cebc8..b33463efd 100644 --- a/trace_streamer/src/parser/ebpf_parser/ebpf_base.h +++ b/trace_streamer/src/parser/ebpf_parser/ebpf_base.h @@ -36,7 +36,7 @@ public: EbpfBase(TraceDataCache *dataCache, const TraceStreamerFilters *ctx); ~EbpfBase(); bool InitEbpfDataParser(EbpfDataReader *reader); - bool EBPFReloadElfSymbolTable(const std::vector> &symbolsFiles); + void EBPFReloadElfSymbolTable(const std::vector> &symbolsFiles); protected: void ParseCallStackData(const uint64_t *userIpsAddr, uint16_t count, uint32_t pid, uint32_t callId); diff --git a/trace_streamer/src/parser/ebpf_parser/file_system_data_parser.cpp b/trace_streamer/src/parser/ebpf_parser/file_system_data_parser.cpp index cd23c3388..b03ae0e8a 100644 --- a/trace_streamer/src/parser/ebpf_parser/file_system_data_parser.cpp +++ b/trace_streamer/src/parser/ebpf_parser/file_system_data_parser.cpp @@ -47,7 +47,7 @@ void FileSystemDataParser::IpAndCallidFind(const FsFixedHeader *fsFixedHeadrAddr currentCallId_ = callChainId_++; } } else { - currentCallId_ = INVALID_UINT64; + currentCallId_ = INVALID_UINT32; } } @@ -100,9 +100,6 @@ size_t FileSystemDataParser::FileWriteOperation(TracerEventToStrIndexMap &tracer void FileSystemDataParser::ParseFileSystemEvent() { - if (!reader_->GetFileSystemEventMap().size()) { - return; - } auto &tracerEventToStrIndexMap = reader_->GetTracerEventToStrIndexMap(); for (auto mapItor = reader_->GetFileSystemEventMap().begin(); mapItor != reader_->GetFileSystemEventMap().end(); mapItor++) { diff --git a/trace_streamer/src/parser/ebpf_parser/paged_memory_data_parser.cpp b/trace_streamer/src/parser/ebpf_parser/paged_memory_data_parser.cpp index 9298676f4..f35d32862 100644 --- a/trace_streamer/src/parser/ebpf_parser/paged_memory_data_parser.cpp +++ b/trace_streamer/src/parser/ebpf_parser/paged_memory_data_parser.cpp @@ -65,9 +65,6 @@ int32_t PagedMemoryDataParser::PagingData(const PagedMemoryFixedHeader *pagedMem void PagedMemoryDataParser::ParsePagedMemoryEvent() { - if (!reader_->GetPagedMemoryMap().size()) { - return; - } for (auto mapItor = reader_->GetPagedMemoryMap().begin(); mapItor != reader_->GetPagedMemoryMap().end(); mapItor++) { streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_EBPF_PAGED_MEMORY, STAT_EVENT_RECEIVED); @@ -80,7 +77,7 @@ void PagedMemoryDataParser::ParsePagedMemoryEvent() pagedMemoryFixedHeadrAddr->nips * SINGLE_IP_SIZE); auto ipsHashValue = hashFun_(ipsToStr); auto value = pidAndipsToCallId_.Find(pagedMemoryFixedHeadrAddr->pid, ipsHashValue); - if (value != INVALID_UINT64) { + if (value != INVALID_UINT32) { callIdExistFlag = true; currentCallId_ = value; } else { diff --git a/trace_streamer/src/parser/hiperf_parser/BUILD.gn b/trace_streamer/src/parser/hiperf_parser/BUILD.gn index 48697bff5..03e3ad65c 100644 --- a/trace_streamer/src/parser/hiperf_parser/BUILD.gn +++ b/trace_streamer/src/parser/hiperf_parser/BUILD.gn @@ -46,7 +46,7 @@ config("hiperf_parser_cfg") { "${THIRD_PARTY}/bounds_checking_function/include", "${THIRD_PARTY}/sqlite/include", "${THIRD_PARTY}/protobuf/src", - "${COMMON_LIBRARY}/c_utils/base/include", + "${COMMON_LIBRARY}/base/include", "${THIRD_PARTY}/googletest/googletest/include", ] include_dirs += [ @@ -60,11 +60,6 @@ config("hiperf_parser_cfg") { "${SRC}/trace_data/trace_stdtype/measure", ] include_dirs += [ - "${THIRD_PARTY}/libunwind/include", - "${THIRD_PARTY}/libunwind/src", - "${PERF_DIR}/hiperf/include", - "${PERF_DIR}/hiperf/include/nonlinux/linux", - "${PERF_DIR}/hiperf/include/nonlinux", "${THIRD_PARTY}/googletest/googletest/include", "${THIRD_PARTY}/perf_include/libbpf", "${THIRD_PARTY}/perf_include/include", @@ -72,25 +67,12 @@ config("hiperf_parser_cfg") { "${THIRD_PARTY}/perf_include/linux", "../hiperf_parser", "../hiperf_parser/include", - "${COMMON_LIBRARY}/c_utils/base/include", + "${COMMON_LIBRARY}/base/include", "${THIRD_PARTY}/sqlite", ] - include_dirs += [ - "${THIRD_PARTY}/perf_include/hiviewdfx/hilog/include", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/cutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxlog", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/common", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/nonlinux", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/include", - ] - include_dirs += [ "${THIRD_PARTY}/libunwind/include" ] if (is_mingw || is_mac) { include_dirs += [ "${THIRD_PARTY}/libbpf/include/uapi" ] } - if (is_mingw) { - cflags = [ "-includeMingW64Fix.h" ] - } } ohos_static_library("hiperf_parser") { @@ -102,9 +84,9 @@ ohos_static_library("hiperf_parser") { "../../filter/perf_filter:hiperf_filter_cfg", ] public_deps = [ - "${PERF_DIR}/hiperf:hiperf_platform_common", + "${PERF_DIR}/hiperf:hiperf_src", "${THIRD_PARTY}/protobuf:protobuf_lite_static", "${THIRD_PARTY}/protobuf:protobuf_static", - "//third_party/perf_include/hiviewdfx:hiviewdfx", + "//third_party/perf_include/hiviewdfx:libfaultloggerd", ] } diff --git a/trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp b/trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp index 2489c601e..127228fa5 100644 --- a/trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp +++ b/trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp @@ -16,7 +16,10 @@ #include "clock_filter_ex.h" #include "file.h" #include "perf_data_filter.h" +#include "perf_file_format.h" #include "stat_filter.h" +#include "utilities.h" +#include namespace SysTuning { namespace TraceStreamer { @@ -66,7 +69,7 @@ uint64_t PerfDataParser::DataProcessingLength(const std::deque &dequeBu &PerfDataParser::SplitPerfWaitForFinish}; if (static_cast(splitState_) >= splitFunc.size()) { - TS_LOGE("Invalid split state %d", splitState_); + TS_LOGE("Invalid split state %d", static_cast(splitState_)); perfSplitError_ = true; SplitDataWithdraw(); return size; @@ -407,25 +410,79 @@ PerfDataParser::~PerfDataParser() static_cast(GetPluginEndTime())); } -bool PerfDataParser::PerfReloadSymbolFiles(std::vector &symbolsPaths) +std::tuple PerfDataParser::GetFileIdWithLikelyFilePath(const std::string &inputFilePath) { - if (access(tmpPerfData_.c_str(), F_OK) != 0) { - TS_LOGE("perf file:%s not exist", tmpPerfData_.c_str()); - return false; + auto perfFilesData = traceDataCache_->GetConstPerfFilesData(); + for (auto row = 0; row < perfFilesData.Size(); row++) { + auto filePath = traceDataCache_->GetDataFromDict(perfFilesData.FilePaths()[row]); + if (EndsWith(filePath, inputFilePath)) { + return std::make_tuple(perfFilesData.FileIds()[row], perfFilesData.FilePaths()[row]); + } } - recordDataReader_ = PerfFileReader::Instance(tmpPerfData_); - report_ = std::make_unique(); - report_->virtualRuntime_.SetSymbolsPaths(symbolsPaths); - if (recordDataReader_ == nullptr) { + return std::make_tuple(INVALID_UINT64, INVALID_DATAINDEX); +} + +bool PerfDataParser::ReloadPerfFile(const std::unique_ptr &symbolsFile, + uint64_t &fileId, + DataIndex &filePathIndex) +{ + std::tie(fileId, filePathIndex) = GetFileIdWithLikelyFilePath(symbolsFile->filePath_); + if (fileId == INVALID_UINT64) { return false; } - if (Reload()) { - Finish(); - return true; - } else { + // clean perf file same fileId data + if (!traceDataCache_->GetPerfFilesData()->EraseFileIdSameData(fileId)) { return false; } + // add new symbol Data to PerfFile table + for (auto dfxSymbol : symbolsFile->GetSymbols()) { + auto symbolNameIndex = traceDataCache_->GetDataIndex(dfxSymbol.GetName()); + traceDataCache_->GetPerfFilesData()->AppendNewPerfFiles(fileId, dfxSymbol.index_, symbolNameIndex, + filePathIndex); + } + return true; } + +void PerfDataParser::ReloadPerfCallChain(const std::unique_ptr &symbolsFile, + uint64_t fileId, + DataIndex filePathIndex) +{ + // Associate perf_callchain with perf_file + auto perfCallChainData = traceDataCache_->GetPerfCallChainData(); + + for (auto row = 0; row < perfCallChainData->Size(); row++) { + if (perfCallChainData->FileIds()[row] == fileId) { + // Get the current call stack's pid and tid + if (!callChainIdToThreadInfo_.count(perfCallChainData->CallChainIds()[row])) { + continue; + } + pid_t pid; + pid_t tid; + std::tie(pid, tid) = callChainIdToThreadInfo_.at(perfCallChainData->CallChainIds()[row]); + // Get VirtualThread object + auto &virtualThread = report_->virtualRuntime_.GetThread(pid, tid); + // Get dfxMap object + auto dfxMap = virtualThread.FindMapByAddr(perfCallChainData->Ips()[row]); + auto vaddr = symbolsFile->GetVaddrInSymbols(perfCallChainData->Ips()[row], dfxMap->begin, dfxMap->offset); + auto dfxSymbol = symbolsFile->GetSymbolWithVaddr(vaddr); + auto nameIndex = traceDataCache_->GetDataIndex(dfxSymbol.GetName()); + perfCallChainData->UpdateSymbolRelatedData(row, dfxSymbol.funcVaddr_, dfxSymbol.index_, nameIndex); + } + } +} + +void PerfDataParser::PerfReloadSymbolFiles(const std::vector> &symbolsFiles) +{ + for (const auto &symbolsFile : symbolsFiles) { + uint64_t fileId; + DataIndex filePathIndex; + if (!ReloadPerfFile(symbolsFile, fileId, filePathIndex)) { + continue; + } + ReloadPerfCallChain(symbolsFile, fileId, filePathIndex); + } +} + bool PerfDataParser::LoadPerfData() { // try load the perf data @@ -464,6 +521,7 @@ bool PerfDataParser::Reload() UpdateEventConfigInfo(); UpdateReportWorkloadInfo(); UpdateCmdlineInfo(); + SetHM(); // update perf Files table UpdateSymbolAndFilesData(); @@ -622,6 +680,7 @@ uint32_t PerfDataParser::UpdateCallChainUnCompressed(const std::unique_ptrdata_.tid)}); uint32_t depth = 0; for (auto frame = sample->callFrames_.rbegin(); frame != sample->callFrames_.rend(); ++frame) { uint64_t fileId = INVALID_UINT64; @@ -673,5 +732,20 @@ void PerfDataParser::Finish() } pidAndStackHashToCallChainId_.Clear(); } + +void PerfDataParser::SetHM() +{ + std::string os = recordDataReader_->GetFeatureString(FEATURE::OSRELEASE); + auto isHM = os.find(HMKERNEL) != std::string::npos; + report_->virtualRuntime_.SetHM(isHM); + if (isHM) { + pid_t devhost = -1; + std::string str = recordDataReader_->GetFeatureString(FEATURE::HIPERF_HM_DEVHOST); + if (str != EMPTY_STRING) { + devhost = std::stoi(str); + } + report_->virtualRuntime_.SetDevhostPid(devhost); + } +} } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/src/parser/hiperf_parser/perf_data_parser.h b/trace_streamer/src/parser/hiperf_parser/perf_data_parser.h index cc422f27f..5b6c28e63 100644 --- a/trace_streamer/src/parser/hiperf_parser/perf_data_parser.h +++ b/trace_streamer/src/parser/hiperf_parser/perf_data_parser.h @@ -58,7 +58,7 @@ public: bool isSplitFile, bool isFinish); void Finish(); - bool PerfReloadSymbolFiles(std::vector &symbolsPaths); + void PerfReloadSymbolFiles(const std::vector> &symbolsFiles); const auto &GetPerfSplitResult() { return splitResult_; @@ -91,6 +91,7 @@ public: } private: + void SetHM(); bool Reload(); bool LoadPerfData(); void UpdateEventConfigInfo(); @@ -101,13 +102,15 @@ private: void UpdateClockType(); bool RecordCallBack(std::unique_ptr record); void UpdatePerfSampleData(uint32_t callChainId, std::unique_ptr &sample); + std::tuple GetFileIdWithLikelyFilePath(const std::string &inputFilePath); + bool ReloadPerfFile(const std::unique_ptr &symbolsFile, uint64_t &fileId, DataIndex &filePathIndex); + void ReloadPerfCallChain(const std::unique_ptr &symbolsFile, uint64_t fileId, DataIndex filePathIndex); uint32_t UpdateCallChainUnCompressed(const std::unique_ptr &sample); SplitPerfState DataLengthProcessing(const std::deque &dequeBuffer, perf_event_header &dataHeader, uint64_t size, uint64_t &processedLen, bool &invalid); - bool PerfSplitCallBack(std::unique_ptr record); uint64_t SplitPerfData(const std::deque &dequeBuffer, uint64_t size, uint64_t offset, bool isFinish); uint64_t DataProcessingLength(const std::deque &dequeBuffer, @@ -175,6 +178,7 @@ private: std::map fileDataDictIdToFileId_ = {}; std::hash hashFun_; DoubleMap pidAndStackHashToCallChainId_; + std::unordered_map> callChainIdToThreadInfo_ = {}; const std::string tmpPerfData_ = "ts_tmp.perf.data"; const std::string cpuOffEventName_ = "sched:sched_switch"; const std::string wakingEventName_ = "sched:sched_waking"; diff --git a/trace_streamer/src/parser/pbreader_parser/BUILD.gn b/trace_streamer/src/parser/pbreader_parser/BUILD.gn index 1752d2d93..cad38ce9f 100644 --- a/trace_streamer/src/parser/pbreader_parser/BUILD.gn +++ b/trace_streamer/src/parser/pbreader_parser/BUILD.gn @@ -74,9 +74,6 @@ ohos_source_set("pbreader_parser_src") { cflags += [ "-D IS_UT" ] } } - if (is_mingw) { - cflags = [ "-includeMingW64Fix.h" ] - } public_configs = [ "../ebpf_parser:ebpf_parser_cfg" ] public_deps = [ "${OHOS_TRACE_STREAMER_PROTOS_DIR}/protos/services:ts_all_type_cpp" ] @@ -127,17 +124,6 @@ ohos_source_set("pbreader_parser_src") { "${OHOS_TRACE_STREAMER_PROTOS_DIR}/protos/services:ts_all_type_cpp_standard", "${THIRD_PARTY}/protobuf:protobuf_lite_static", "${THIRD_PARTY}/protobuf:protobuf_static", - "//third_party/perf_include/hiviewdfx:hiviewdfx", + "//third_party/perf_include/hiviewdfx:libfaultloggerd", ] - if (!use_wasm && !is_win && !is_mingw && !is_mac && !is_test) { - if (!is_independent_compile) { - if (target_cpu == "arm64") { - public_deps += [ "//third_party/libunwind:unwind_source_arm64_opt" ] - } else { - public_deps += [ "//third_party/libunwind:unwind_source_${target_cpu}" ] - } - } else { - public_deps += [ "${THIRD_PARTY}/libunwind:libunwind" ] - } - } } diff --git a/trace_streamer/src/parser/pbreader_parser/htrace_parser/htrace_event_parser.cpp b/trace_streamer/src/parser/pbreader_parser/htrace_parser/htrace_event_parser.cpp index 2ffb5fcc2..ad373e46b 100644 --- a/trace_streamer/src/parser/pbreader_parser/htrace_parser/htrace_event_parser.cpp +++ b/trace_streamer/src/parser/pbreader_parser/htrace_parser/htrace_event_parser.cpp @@ -30,6 +30,7 @@ #include "ipi.pbreader.h" #include "irq_filter.h" #include "irq.pbreader.h" +#include "dma_fence.pbreader.h" #include "measure_filter.h" #include "oom.pbreader.h" #include "power.pbreader.h" @@ -96,6 +97,14 @@ void HtraceEventParser::InterruptEventInitialization() std::bind(&HtraceEventParser::SoftIrqEntryEvent, this, std::placeholders::_1)); eventToFunctionMap_.emplace(TRACE_EVENT_SOFTIRQ_EXIT, std::bind(&HtraceEventParser::SoftIrqExitEvent, this, std::placeholders::_1)); + eventToFunctionMap_.emplace(TRACE_EVENT_DMA_FENCE_INIT, + std::bind(&HtraceEventParser::DmaFenceInitEvent, this, std::placeholders::_1)); + eventToFunctionMap_.emplace(TRACE_EVENT_DMA_FENCE_DESTROY, + std::bind(&HtraceEventParser::DmaFenceDestroyEvent, this, std::placeholders::_1)); + eventToFunctionMap_.emplace(TRACE_EVENT_DMA_FENCE_ENABLE, + std::bind(&HtraceEventParser::DmaFenceEnableEvent, this, std::placeholders::_1)); + eventToFunctionMap_.emplace(TRACE_EVENT_DMA_FENCE_SIGNALED, + std::bind(&HtraceEventParser::DmaFenceSignaledEvent, this, std::placeholders::_1)); } void HtraceEventParser::ClockEventInitialization() @@ -323,6 +332,17 @@ bool HtraceEventParser::InterruptEventSet(const ProtoReader::FtraceEvent_Reader judgment = BytesViewEventInfo(bytesView, event.softirq_exit_format(), eventInfo, TRACE_EVENT_SOFTIRQ_EXIT); } else if (event.has_softirq_entry_format()) { judgment = BytesViewEventInfo(bytesView, event.softirq_entry_format(), eventInfo, TRACE_EVENT_SOFTIRQ_ENTRY); + } else if (event.has_dma_fence_init_format()) { + judgment = BytesViewEventInfo(bytesView, event.dma_fence_init_format(), eventInfo, TRACE_EVENT_DMA_FENCE_INIT); + } else if (event.has_dma_fence_destroy_format()) { + judgment = + BytesViewEventInfo(bytesView, event.dma_fence_destroy_format(), eventInfo, TRACE_EVENT_DMA_FENCE_DESTROY); + } else if (event.has_dma_fence_enable_signal_format()) { + judgment = BytesViewEventInfo(bytesView, event.dma_fence_enable_signal_format(), eventInfo, + TRACE_EVENT_DMA_FENCE_ENABLE); + } else if (event.has_dma_fence_signaled_format()) { + judgment = + BytesViewEventInfo(bytesView, event.dma_fence_signaled_format(), eventInfo, TRACE_EVENT_DMA_FENCE_SIGNALED); } return judgment; @@ -887,6 +907,62 @@ bool HtraceEventParser::SoftIrqExitEvent(const EventInfo &event) const streamFilters_->irqFilter_->SoftIrqExit(event.timeStamp, event.cpu, static_cast(msg.vec())); return true; } +bool HtraceEventParser::DmaFenceInitEvent(const EventInfo &event) const +{ + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE_INIT, STAT_EVENT_RECEIVED); + ProtoReader::DmaFenceInitFormat_Reader msg(event.detail); + DmaFenceRow dmaFenceRow = {event.timeStamp, + 0, + dmaFenceInitName_, + traceDataCache_->GetDataIndex(msg.driver().ToStdString()), + traceDataCache_->GetDataIndex(msg.timeline().ToStdString()), + static_cast(msg.context()), + static_cast(msg.seqno())}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} +bool HtraceEventParser::DmaFenceDestroyEvent(const EventInfo &event) const +{ + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE_DESTROY, STAT_EVENT_RECEIVED); + ProtoReader::DmaFenceDestroyFormat_Reader msg(event.detail); + DmaFenceRow dmaFenceRow = {event.timeStamp, + 0, + dmaFenceDestroyName_, + traceDataCache_->GetDataIndex(msg.driver().ToStdString()), + traceDataCache_->GetDataIndex(msg.timeline().ToStdString()), + static_cast(msg.context()), + static_cast(msg.seqno())}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} +bool HtraceEventParser::DmaFenceEnableEvent(const EventInfo &event) const +{ + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE_ENABLE, STAT_EVENT_RECEIVED); + ProtoReader::DmaFenceEnableSignalFormat_Reader msg(event.detail); + DmaFenceRow dmaFenceRow = {event.timeStamp, + 0, + dmaFenceEnableName_, + traceDataCache_->GetDataIndex(msg.driver().ToStdString()), + traceDataCache_->GetDataIndex(msg.timeline().ToStdString()), + static_cast(msg.context()), + static_cast(msg.seqno())}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} +bool HtraceEventParser::DmaFenceSignaledEvent(const EventInfo &event) const +{ + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE_SIGNALED, STAT_EVENT_RECEIVED); + ProtoReader::DmaFenceSignaledFormat_Reader msg(event.detail); + DmaFenceRow dmaFenceRow = {event.timeStamp, + 0, + dmaFenceSignaledName_, + traceDataCache_->GetDataIndex(msg.driver().ToStdString()), + traceDataCache_->GetDataIndex(msg.timeline().ToStdString()), + static_cast(msg.context()), + static_cast(msg.seqno())}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} bool HtraceEventParser::SysEnterEvent(const EventInfo &event) const { streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_SYS_ENTRY, STAT_EVENT_RECEIVED); @@ -908,7 +984,8 @@ bool HtraceEventParser::OomScoreAdjUpdate(const EventInfo &event) const { streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_OOM_SCORE_ADJ_UPDATE, STAT_EVENT_RECEIVED); ProtoReader::OomScoreAdjUpdateFormat_Reader msg(event.detail); - streamFilters_->processMeasureFilter_->AppendNewMeasureData(msg.pid(), oomScoreAdjName_, event.timeStamp, + auto ipid = streamFilters_->processFilter_->GetInternalPid(msg.pid()); + streamFilters_->processMeasureFilter_->AppendNewMeasureData(ipid, oomScoreAdjName_, event.timeStamp, msg.oom_score_adj()); return true; } diff --git a/trace_streamer/src/parser/pbreader_parser/htrace_parser/htrace_event_parser.h b/trace_streamer/src/parser/pbreader_parser/htrace_parser/htrace_event_parser.h index fee2ec4fc..6a16997e3 100644 --- a/trace_streamer/src/parser/pbreader_parser/htrace_parser/htrace_event_parser.h +++ b/trace_streamer/src/parser/pbreader_parser/htrace_parser/htrace_event_parser.h @@ -140,6 +140,10 @@ private: bool SoftIrqEntryEvent(const EventInfo &event) const; bool SoftIrqRaiseEvent(const EventInfo &event) const; bool SoftIrqExitEvent(const EventInfo &event) const; + bool DmaFenceInitEvent(const EventInfo &event) const; + bool DmaFenceDestroyEvent(const EventInfo &event) const; + bool DmaFenceEnableEvent(const EventInfo &event) const; + bool DmaFenceSignaledEvent(const EventInfo &event) const; bool SysEnterEvent(const EventInfo &event) const; bool SysExitEvent(const EventInfo &event) const; bool OomScoreAdjUpdate(const EventInfo &event) const; @@ -163,6 +167,10 @@ private: const DataIndex sysEnterName_ = traceDataCache_->GetDataIndex("sys_enter"); const DataIndex sysExitName_ = traceDataCache_->GetDataIndex("sys_exit"); const DataIndex oomScoreAdjName_ = traceDataCache_->GetDataIndex("oom_score_adj"); + const DataIndex dmaFenceInitName_ = traceDataCache_->GetDataIndex("dma_fence_init"); + const DataIndex dmaFenceDestroyName_ = traceDataCache_->GetDataIndex("dma_fence_destroy"); + const DataIndex dmaFenceEnableName_ = traceDataCache_->GetDataIndex("dma_fence_enable_signal"); + const DataIndex dmaFenceSignaledName_ = traceDataCache_->GetDataIndex("dma_fence_signaled"); TraceStreamerConfig config_{}; std::atomic clock_{TS_CLOCK_BOOTTIME}; std::mutex mutex_; diff --git a/trace_streamer/src/parser/pbreader_parser/native_hook_parser/BUILD.gn b/trace_streamer/src/parser/pbreader_parser/native_hook_parser/BUILD.gn index a9f2279d0..0e512554e 100644 --- a/trace_streamer/src/parser/pbreader_parser/native_hook_parser/BUILD.gn +++ b/trace_streamer/src/parser/pbreader_parser/native_hook_parser/BUILD.gn @@ -29,7 +29,7 @@ ohos_static_library("native_hook_parser") { "${OHOS_TRACE_STREAMER_PROTOS_DIR}/protos/services:ts_all_type_cpp", "${OHOS_TRACE_STREAMER_PROTOS_DIR}/protos/types/plugins/ftrace_data/${device_kernel_version}:ftrace_data_reader", "${OHOS_TRACE_STREAMER_PROTOS_DIR}/protos/types/plugins/native_hook:native_hook_data_reader", - "${PERF_DIR}/hiperf:hiperf_platform_common", + "${PERF_DIR}/hiperf:hiperf_src", ] public_configs = [ ":native_hook_parser_cfg", diff --git a/trace_streamer/src/parser/pbreader_parser/native_hook_parser/pbreader_native_hook_parser.h b/trace_streamer/src/parser/pbreader_parser/native_hook_parser/pbreader_native_hook_parser.h index c7fe533aa..e13a90e41 100644 --- a/trace_streamer/src/parser/pbreader_parser/native_hook_parser/pbreader_native_hook_parser.h +++ b/trace_streamer/src/parser/pbreader_parser/native_hook_parser/pbreader_native_hook_parser.h @@ -36,9 +36,9 @@ public: void FinishSplitNativeHook(); void FinishParseNativeHookData(); void Finish(); - bool NativeHookReloadElfSymbolTable(const std::vector> &symbolsFile) + void NativeHookReloadElfSymbolTable(const std::vector> &symbolsFile) { - return nativeHookFilter_->NativeHookReloadElfSymbolTable(symbolsFile); + nativeHookFilter_->NativeHookReloadElfSymbolTable(symbolsFile); } void UpdataOfflineSymbolizationMode(bool isOfflineSymbolizationMode) { diff --git a/trace_streamer/src/parser/pbreader_parser/pbreader_parser.cpp b/trace_streamer/src/parser/pbreader_parser/pbreader_parser.cpp index 6f742dfe4..f7badb159 100644 --- a/trace_streamer/src/parser/pbreader_parser/pbreader_parser.cpp +++ b/trace_streamer/src/parser/pbreader_parser/pbreader_parser.cpp @@ -196,18 +196,16 @@ PbreaderParser::~PbreaderParser() bool PbreaderParser::ReparseSymbolFilesAndResymbolization(std::string &symbolsPath, std::vector &symbolsPaths) { - std::vector dirs; auto parseStatus = false; - for (auto file : symbolsPaths) { - auto dir = file.substr(0, file.find_last_of("/\\")); - dirs.emplace_back(dir); - } -#ifdef ENABLE_HIPERF - parseStatus = perfDataParser_->PerfReloadSymbolFiles(dirs); -#endif #if defined(ENABLE_HIPERF) || defined(ENABLE_NATIVE_HOOK) || defined(ENABLE_EBPF) ParserFileSO(symbolsPath, symbolsPaths); #endif +#ifdef ENABLE_HIPERF + if (traceDataCache_->GetPerfFilesData()->Size() > 0) { + perfDataParser_->PerfReloadSymbolFiles(symbolsFiles_); + parseStatus = true; + } +#endif #ifdef ENABLE_NATIVE_HOOK if (traceDataCache_->GetNativeHookFrameData()->Size() > 0) { pbreaderNativeHookParser_->NativeHookReloadElfSymbolTable(symbolsFiles_); @@ -302,6 +300,9 @@ void PbreaderParser::WaitForParserEnd() hasGotHeader_ = false; WaitForHPluginParserEnd(); WaitForOtherPluginParserEnd(); +#if defined(ENABLE_HTRACE) && defined(ENABLE_NATIVE_HOOK) && defined(ENABLE_HIPERF) + ParseNapiAsync(); +#endif traceDataCache_->GetDataSourceClockIdData()->Finish(); dataSegArray_.reset(); processedDataLen_ = 0; @@ -1135,5 +1136,148 @@ bool PbreaderParser::InitProfilerTraceFileHeader() pbreaderClockDetailParser_->Parse(pHeader); return true; } + +#if defined(ENABLE_HTRACE) && defined(ENABLE_NATIVE_HOOK) && defined(ENABLE_HIPERF) +void PbreaderParser::ParseNapiAsync() +{ + // 将native memory中存在的traceid取出, 并记录其对应的callstackid + std::unordered_map traceidToCallchainidMap; + GetTraceidInfoFromNativeHook(traceidToCallchainidMap); + + // 从callstack表中获取所有的traceid, 根据其所属的itid将SliceInfo存入对应queue + std::unordered_map> itidToCallstackIdsMap; + GetTraceidInfoFromCallstack(traceidToCallchainidMap, itidToCallstackIdsMap); + + // 筛选出包含NativeAsyncWork::AsyncWorkCallback的函数栈的callchainid, 将其存入callchainIdSet + std::unordered_set callchainIdSet; + GetCallchainIdSetFromHiperf(callchainIdSet); + + DumpDataFromHiperf(traceidToCallchainidMap, callchainIdSet, itidToCallstackIdsMap); +} + +void PbreaderParser::GetTraceidInfoFromNativeHook(std::unordered_map &traceidToCallchainidMap) +{ + auto nativeHook = traceDataCache_->GetConstNativeHookData(); + std::string preWord("napi:"); + for (int i = 0; i < nativeHook.Size(); i++) { + auto subType = nativeHook.SubTypes()[i]; + if (subType == INVALID_UINT64) { + continue; + } + auto subTypeStr = traceDataCache_->GetDataFromDict(subType); + if (!StartWith(subTypeStr, preWord)) { + continue; + } + auto pos = subTypeStr.find(preWord) + preWord.size(); + auto traceidStr = subTypeStr.substr(pos, subTypeStr.find_last_of(':') - pos); + auto traceidIndex = traceDataCache_->GetDataIndex(traceidStr); + traceidToCallchainidMap.emplace(std::move(traceidStr), nativeHook.CallChainIds()[i]); + } +} + +void PbreaderParser::GetTraceidInfoFromCallstack( + const std::unordered_map &traceidToCallchainidMap, + std::unordered_map> &itidToCallstackIdsMap) +{ + auto callStack = traceDataCache_->GetConstInternalSlicesData(); + std::string preWord("traceid:"); + std::string invalidTraceidStr("0x0"); + for (int i = 0; i < callStack.Size(); i++) { + auto name = traceDataCache_->GetDataFromDict(callStack.NamesData()[i]); + if (!StartWith(name, "H:Napi execute, name:") || callStack.DursData()[i] == INVALID_UINT64) { + continue; + } + auto traceidStr = name.substr(name.find(preWord) + preWord.size()); + if (traceidStr == invalidTraceidStr) { + continue; + } + if (traceidToCallchainidMap.find(traceidStr) == traceidToCallchainidMap.end()) { + continue; + } + auto iter = itidToCallstackIdsMap.find(callStack.CallIds()[i]); + if (iter == itidToCallstackIdsMap.end()) { + itidToCallstackIdsMap.emplace(callStack.CallIds()[i], std::queue()); + iter = itidToCallstackIdsMap.find(callStack.CallIds()[i]); + } + iter->second.emplace(callStack.TimeStampData()[i], callStack.TimeStampData()[i] + callStack.DursData()[i], + traceidStr); + } +} + +void PbreaderParser::GetCallchainIdSetFromHiperf(std::unordered_set &callchainIdSet) +{ + auto perfCallChain = traceDataCache_->GetConstPerfCallChainData(); + std::string asyncWork("NativeAsyncWork::AsyncWorkCallback"); + for (int i = 0; i < perfCallChain.Size(); i++) { + auto callchainId = perfCallChain.CallChainIds()[i]; + if (callchainIdSet.find(callchainId) != callchainIdSet.end()) { + continue; + } + auto nameIndex = perfCallChain.Names()[i]; + if (nameIndex == INVALID_UINT64) { + continue; + } + auto name = traceDataCache_->GetDataFromDict(nameIndex); + if (name.find(asyncWork) != std::string::npos) { + callchainIdSet.emplace(perfCallChain.CallChainIds()[i]); + } + } +} + +void PbreaderParser::DumpDataFromHiperf(const std::unordered_map &traceidToCallchainidMap, + const std::unordered_set &callchainIdSet, + std::unordered_map> &itidToCallstackIdsMap) +{ + auto perfThread = traceDataCache_->GetConstPerfThreadData(); + std::unordered_map tidToPidMap; + for (size_t i = 0; i < perfThread.Size(); i++) { + tidToPidMap.emplace(perfThread.Tids()[i], perfThread.Pids()[i]); + } + auto perfSample = traceDataCache_->GetConstPerfSampleData(); + auto callStack = traceDataCache_->GetConstInternalSlicesData(); + for (size_t i = 0; i < perfSample.Size(); i++) { + // callchainid未命中, 即当前栈不包含NativeAsyncWork::AsyncWorkCallback + if (callchainIdSet.find(perfSample.SampleIds()[i]) == callchainIdSet.end()) { + continue; + } + // 根据tid和tsPerfSample查询对应的SliceInfo + auto itid = streamFilters_->processFilter_->GetInternalTid(perfSample.Tids()[i]); + if (itidToCallstackIdsMap.find(itid) == itidToCallstackIdsMap.end()) { + continue; + } + auto tsPerfSample = perfSample.TimestampTraces()[i]; + auto queue = itidToCallstackIdsMap.at(itid); + while (!queue.empty() && queue.front().tsEnd_ < tsPerfSample) { + queue.pop(); + } + if (queue.empty() || tsPerfSample < queue.front().tsBegin_) { + continue; + } + // 根据traceid查询native侧的callchainid + auto iterNative = traceidToCallchainidMap.find(queue.front().traceid_); + if (iterNative == traceidToCallchainidMap.end()) { + continue; + } + // 根据tid查询pid + auto iterPid = tidToPidMap.find(perfSample.Tids()[i]); + if (iterPid == tidToPidMap.end()) { + continue; + } + PerfNapiAsyncRow perfNapiAsyncRow{ + .timeStamp = tsPerfSample, + .traceid = traceDataCache_->GetDataIndex(queue.front().traceid_), + .cpuId = static_cast(perfSample.CpuIds()[i]), + .threadId = perfSample.Tids()[i], + .processId = iterPid->second, + .callerCallchainid = iterNative->second, + .calleeCallchainid = perfSample.SampleIds()[i], + .perfSampleId = i, + .eventCount = perfSample.EventCounts()[i], + .eventTypeId = perfSample.EventTypeIds()[i], + }; + traceDataCache_->GetPerfNapiAsyncData()->AppendNewPerfNapiAsync(perfNapiAsyncRow); + } +} +#endif } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/src/parser/pbreader_parser/pbreader_parser.h b/trace_streamer/src/parser/pbreader_parser/pbreader_parser.h index f1a3b8319..644ab7c13 100644 --- a/trace_streamer/src/parser/pbreader_parser/pbreader_parser.h +++ b/trace_streamer/src/parser/pbreader_parser/pbreader_parser.h @@ -18,9 +18,12 @@ #include #include #include +#include #include #include #include +#include +#include #include "common_types.h" #include "common_types.pbreader.h" #include "clock_filter_ex.h" @@ -86,6 +89,15 @@ namespace SysTuning { namespace TraceStreamer { using namespace SysTuning::base; using namespace OHOS::Developtools::HiPerf; +#if defined(ENABLE_HTRACE) && defined(ENABLE_NATIVE_HOOK) && defined(ENABLE_HIPERF) +struct SliceInfo { + SliceInfo(uint64_t tsBegin, uint64_t tsEnd, const std::string &traceid) + : tsBegin_(tsBegin), tsEnd_(tsEnd), traceid_(traceid){}; + uint64_t tsBegin_ = INVALID_UINT64; + uint64_t tsEnd_ = INVALID_UINT64; + std::string traceid_; +}; +#endif class PbreaderParser : public ParserBase, public HtracePluginTimeParser { public: PbreaderParser(TraceDataCache *dataCache, const TraceStreamerFilters *filters); @@ -246,6 +258,16 @@ private: const ProtoReader::ProfilerPluginData_Reader &pluginDataZero, bool isSplitFile); bool SpliteDataBySegment(DataIndex pluginNameIndex, PbreaderDataSegment &dataSeg); +#if defined(ENABLE_HTRACE) && defined(ENABLE_NATIVE_HOOK) && defined(ENABLE_HIPERF) + void ParseNapiAsync(); + void GetTraceidInfoFromCallstack(const std::unordered_map &traceidToCallchainidMap, + std::unordered_map> &itidToCallstackIdsMap); + void GetTraceidInfoFromNativeHook(std::unordered_map &traceidToCallchainidMap); + void GetCallchainIdSetFromHiperf(std::unordered_set &callchainIdSet); + void DumpDataFromHiperf(const std::unordered_map &traceidToCallchainidMap, + const std::unordered_set &callchainIdSet, + std::unordered_map> &itidToCallstackIdsMap); +#endif ProfilerTraceFileHeader profilerTraceFileHeader_; uint32_t profilerDataType_ = ProfilerTraceFileHeader::UNKNOW_TYPE; uint64_t profilerDataLength_ = 0; diff --git a/trace_streamer/src/parser/print_event_parser.cpp b/trace_streamer/src/parser/print_event_parser.cpp index e28f533b4..6b3f73f69 100644 --- a/trace_streamer/src/parser/print_event_parser.cpp +++ b/trace_streamer/src/parser/print_event_parser.cpp @@ -32,6 +32,8 @@ PrintEventParser::PrintEventParser(TraceDataCache *dataCache, const TraceStreame std::placeholders::_3)}, {rsOnDoCompositionEvent_, bind(&PrintEventParser::RSReciveOnDoComposition, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)}, + {onVsyncEvent_, bind(&PrintEventParser::OnVsyncEvent, this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)}, {marshRwTransactionData_, bind(&PrintEventParser::OnRwTransaction, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)}, {rsMainThreadProcessCmd_, bind(&PrintEventParser::OnMainThreadProcessCmd, this, std::placeholders::_1, @@ -196,7 +198,7 @@ void PrintEventParser::Finish() { eventToFrameFunctionMap_.clear(); frameCallIds_.clear(); - vsyncSliceIds_.clear(); + vsyncSliceMap_.clear(); streamFilters_->animationFilter_->Clear(); streamFilters_->frameFilter_->Clear(); } @@ -253,8 +255,18 @@ ParseResult PrintEventParser::HandlerB(std::string_view pointStr, TracePoint &ou TS_LOGD("point name is empty!"); return PARSE_ERROR; } - // Use ## to differentiate distributed data - if (outPoint.name_.find("##") == std::string::npos) { + // Distributed data: + // <...>-357 (-------) .... 174330.287420: tracing_mark_write: + // B|1298|H:[8b00e96b2,2,1]#C##napi::NativeAsyncWork::QueueWithQos + std::smatch matcheLine; + bool matched = std::regex_match(outPoint.name_, matcheLine, distributeMatcher_); + if (matched) { + size_t index = 0; + outPoint.chainId_ = matcheLine[++index].str(); + outPoint.spanId_ = matcheLine[++index].str(); + outPoint.parentSpanId_ = matcheLine[++index].str(); + outPoint.flag_ = matcheLine[++index].str(); + } else { auto space = outPoint.name_.find(' '); if (space != std::string::npos) { outPoint.funcPrefix_ = outPoint.name_.substr(0, space); @@ -263,20 +275,6 @@ ParseResult PrintEventParser::HandlerB(std::string_view pointStr, TracePoint &ou } else { outPoint.funcPrefixId_ = traceDataCache_->GetDataIndex(outPoint.name_); } - return PARSE_SUCCESS; - } - // Resolve distributed calls - // the normal data mybe like: - // system-1298 ( 1298) [001] ...1 174330.287420: tracing_mark_write: B|1298|H:[8b00e96b2,2,1]#C##decodeFrame" - const std::regex distributeMatcher = std::regex(R"(H:\[([a-z0-9]+),([a-z0-9]+),([a-z0-9]+)\]#([CS]?)##(.*))"); - std::smatch matcheLine; - bool matched = std::regex_match(outPoint.name_, matcheLine, distributeMatcher); - if (matched) { - size_t index = 0; - outPoint.chainId_ = matcheLine[++index].str(); - outPoint.spanId_ = matcheLine[++index].str(); - outPoint.parentSpanId_ = matcheLine[++index].str(); - outPoint.flag_ = matcheLine[++index].str(); } return PARSE_SUCCESS; } @@ -327,7 +325,26 @@ bool PrintEventParser::ReciveVsync(size_t callStackRow, std::string &args, const } } streamFilters_->frameFilter_->BeginVsyncEvent(line, now, expectEnd, vsyncId, callStackRow); - vsyncSliceIds_.push_back(callStackRow); + auto iTid = streamFilters_->processFilter_->GetInternalTid(line.pid); + if (vsyncSliceMap_.count(iTid)) { + vsyncSliceMap_[iTid].push_back(callStackRow); + } else { + vsyncSliceMap_[iTid] = {callStackRow}; + } + return true; +} +bool PrintEventParser::OnVsyncEvent(size_t callStackRow, std::string &args, const BytraceLine &line) +{ + Unused(args); + auto iTid = streamFilters_->processFilter_->GetInternalTid(line.pid); + if (!vsyncSliceMap_.count(iTid)) { + return false; + } + // when there are mutiple nested OnVsyncEvent,only handle the OnvsyncEvent of the next layer under ReceiveVsync + if (vsyncSliceMap_[iTid].size() >= maxVsyncEventSize_) { + return false; + } + vsyncSliceMap_[iTid].push_back(callStackRow); return true; } bool PrintEventParser::RSReciveOnDoComposition(size_t callStackRow, std::string &args, const BytraceLine &line) @@ -351,7 +368,7 @@ bool PrintEventParser::OnRwTransaction(size_t callStackRow, std::string &args, c } bool PrintEventParser::OnMainThreadProcessCmd(size_t callStackRow, std::string &args, const BytraceLine &line) { - std::sregex_iterator it(args.begin(), args.end(), mainProcessCmdPattern); + std::sregex_iterator it(args.begin(), args.end(), mainProcessCmdPattern_); std::sregex_iterator end; std::vector frames; while (it != end) { @@ -378,12 +395,14 @@ void PrintEventParser::HandleFrameSliceEndEvent(uint64_t ts, uint64_t pid, uint6 { // it can be frame or slice auto iTid = streamFilters_->processFilter_->GetInternalTid(tid); - auto pos = std::find(vsyncSliceIds_.begin(), vsyncSliceIds_.end(), callStackRow); - if (pos != vsyncSliceIds_.end()) { - if (!streamFilters_->frameFilter_->EndVsyncEvent(ts, iTid)) { - streamFilters_->statFilter_->IncreaseStat(TRACE_VSYNC, STAT_EVENT_NOTMATCH); + if (vsyncSliceMap_.count(iTid)) { + auto pos = std::find(vsyncSliceMap_[iTid].begin(), vsyncSliceMap_[iTid].end(), callStackRow); + if (pos != vsyncSliceMap_[iTid].end()) { + if (!streamFilters_->frameFilter_->EndVsyncEvent(ts, iTid)) { + streamFilters_->statFilter_->IncreaseStat(TRACE_VSYNC, STAT_EVENT_NOTMATCH); + } + vsyncSliceMap_[iTid].erase(pos); } - vsyncSliceIds_.erase(pos); } return; } diff --git a/trace_streamer/src/parser/print_event_parser.h b/trace_streamer/src/parser/print_event_parser.h index 2bd70edb6..6b26d035a 100644 --- a/trace_streamer/src/parser/print_event_parser.h +++ b/trace_streamer/src/parser/print_event_parser.h @@ -79,11 +79,13 @@ private: bool OnRwTransaction(size_t callStackRow, std::string &args, const BytraceLine &line); bool OnMainThreadProcessCmd(size_t callStackRow, std::string &args, const BytraceLine &line); bool OnFrameQueueStart(uint64_t ts, size_t callStackRow, uint64_t pid); + bool OnVsyncEvent(size_t callStackRow, std::string &args, const BytraceLine &line); private: std::map eventToFrameFunctionMap_ = {}; TraceStreamerConfig config_{}; const DataIndex recvievVsync_ = traceDataCache_->GetDataIndex("H:ReceiveVsync"); + const DataIndex onVsyncEvent_ = traceDataCache_->GetDataIndex("H:OnVsyncEvent"); const std::string rsOnDoCompositionStr_ = "H:RSMainThread::DoComposition"; DataIndex rsOnDoCompositionEvent_ = INVALID_DATAINDEX; const std::string onFrameQueeuStartEvent_ = "H:M: Frame queued"; @@ -92,11 +94,13 @@ private: const DataIndex rsMainThreadProcessCmd_ = traceDataCache_->GetDataIndex("H:RSMainThread::ProcessCommandUni"); const std::regex recvVsyncPattern_ = std::regex("(\\w+):(\\w+)"); const std::regex transFlagPattern_ = std::regex("transactionFlag:\\[(\\d+),(\\d+)\\]"); - const std::regex mainProcessCmdPattern = std::regex("\\[(\\d+),(\\d+)\\]"); + const std::regex mainProcessCmdPattern_ = std::regex("\\[(\\d+),(\\d+)\\]"); + const std::regex distributeMatcher_ = std::regex(R"(H:\[([a-z0-9]+),([a-z0-9]+),([a-z0-9]+)\]#([CS]?)##(.*))"); std::vector frameCallIds_ = {}; - std::vector vsyncSliceIds_ = {}; + std::unordered_map> vsyncSliceMap_ = {}; TraceFileType traceType_ = TRACE_FILETYPE_H_TRACE; BuiltinClocks clock_ = TS_CLOCK_BOOTTIME; + const uint32_t maxVsyncEventSize_ = 2; // if convert vsync's now and expectEnd bool convertVsyncTs_ = true; }; diff --git a/trace_streamer/src/parser/ptreader_parser/bytrace_parser/bytrace_event_parser.cpp b/trace_streamer/src/parser/ptreader_parser/bytrace_parser/bytrace_event_parser.cpp index c66641bcd..1557e9fde 100644 --- a/trace_streamer/src/parser/ptreader_parser/bytrace_parser/bytrace_event_parser.cpp +++ b/trace_streamer/src/parser/ptreader_parser/bytrace_parser/bytrace_event_parser.cpp @@ -98,6 +98,18 @@ void BytraceEventParser::InterruptEventInitialization() eventToFunctionMap_.emplace( config_.eventNameMap_.at(TRACE_EVENT_SOFTIRQ_EXIT), bind(&BytraceEventParser::SoftIrqExitEvent, this, std::placeholders::_1, std::placeholders::_2)); + eventToFunctionMap_.emplace( + config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_INIT), + bind(&BytraceEventParser::DmaFenceEvent, this, std::placeholders::_1, std::placeholders::_2)); + eventToFunctionMap_.emplace( + config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_DESTROY), + bind(&BytraceEventParser::DmaFenceEvent, this, std::placeholders::_1, std::placeholders::_2)); + eventToFunctionMap_.emplace( + config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_ENABLE), + bind(&BytraceEventParser::DmaFenceEvent, this, std::placeholders::_1, std::placeholders::_2)); + eventToFunctionMap_.emplace( + config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_SIGNALED), + bind(&BytraceEventParser::DmaFenceEvent, this, std::placeholders::_1, std::placeholders::_2)); } void BytraceEventParser::ClockEventInitialization() @@ -642,7 +654,28 @@ bool BytraceEventParser::SoftIrqExitEvent(const ArgsMap &args, const BytraceLine streamFilters_->irqFilter_->SoftIrqExit(line.ts, line.cpu, vec.value()); return true; } - +bool BytraceEventParser::DmaFenceEvent(const ArgsMap &args, const BytraceLine &line) const +{ + if (args.empty() || args.size() < MIN_DMA_FENCE_ARGS_COUNT) { + TS_LOGD("Failed to dma fence event,no args or args size <4"); + streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_DMA_FENCE, STAT_EVENT_DATA_INVALID); + return false; + } + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE, STAT_EVENT_RECEIVED); + auto driverStr = std::string_view(args.at("driver")); + auto timelineStr = std::string_view(args.at("timeline")); + auto context = base::StrToInt(args.at("context")); + auto seqno = base::StrToInt(args.at("seqno")); + DmaFenceRow dmaFenceRow = {line.ts, + 0, + traceDataCache_->GetDataIndex(line.eventName), + traceDataCache_->GetDataIndex(driverStr), + traceDataCache_->GetDataIndex(timelineStr), + context.value(), + seqno.value()}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} bool BytraceEventParser::BinderTransaction(const ArgsMap &args, const BytraceLine &line) const { if (args.empty() || args.size() < MIN_BINDER_TRANSACTION_ARGS_COUNT) { diff --git a/trace_streamer/src/parser/ptreader_parser/bytrace_parser/bytrace_event_parser.h b/trace_streamer/src/parser/ptreader_parser/bytrace_parser/bytrace_event_parser.h index a037d5d54..9021d221f 100644 --- a/trace_streamer/src/parser/ptreader_parser/bytrace_parser/bytrace_event_parser.h +++ b/trace_streamer/src/parser/ptreader_parser/bytrace_parser/bytrace_event_parser.h @@ -74,6 +74,7 @@ private: bool SoftIrqRaiseEvent(const ArgsMap &args, const BytraceLine &line) const; bool SoftIrqEntryEvent(const ArgsMap &args, const BytraceLine &line) const; bool SoftIrqExitEvent(const ArgsMap &args, const BytraceLine &line) const; + bool DmaFenceEvent(const ArgsMap &args, const BytraceLine &line) const; bool BinderTransaction(const ArgsMap &args, const BytraceLine &line) const; bool BinderTransactionReceived(const ArgsMap &args, const BytraceLine &line) const; bool BinderTransactionAllocBufEvent(const ArgsMap &args, const BytraceLine &line) const; @@ -101,6 +102,7 @@ private: const uint32_t MIN_IRQ_HANDLER_EXIT_ARGS_COUNT = 2; const uint32_t MIN_SOFTIRQ_ENTRY_ARGS_COUNT = 2; const uint32_t MIN_SOFTIRQ_EXIT_ARGS_COUNT = 2; + const uint32_t MIN_DMA_FENCE_ARGS_COUNT = 4; const uint32_t MIN_BINDER_TRANSACTION_ARGS_COUNT = 7; const uint32_t MIN_BINDER_TRANSACTION_RECEIVED_ARGS_COUNT = 1; const uint32_t MIN_BINDER_TRANSACTION_ALLOC_BUF_ARGS_COUNT = 3; diff --git a/trace_streamer/src/parser/rawtrace_parser/BUILD.gn b/trace_streamer/src/parser/rawtrace_parser/BUILD.gn index 35e990dd6..806807529 100755 --- a/trace_streamer/src/parser/rawtrace_parser/BUILD.gn +++ b/trace_streamer/src/parser/rawtrace_parser/BUILD.gn @@ -28,11 +28,8 @@ config("rawtrace_parser_comm") { "${THIRD_PARTY}/sqlite/include", "${THIRD_PARTY}/bounds_checking_function/include", "${THIRD_PARTY}/json/single_include/nlohmann", - "${PERF_DIR}/hiperf/include", - "${PERF_DIR}/hiperf/include/nonlinux/linux", - "${PERF_DIR}/hiperf/include/nonlinux", "${PERF_DIR}/profiler/device/plugins/ftrace_plugin/include", - "${COMMON_LIBRARY}/c_utils/base/include", + "${COMMON_LIBRARY}/base/include", ] include_dirs += [ "${SRC}/trace_data/trace_stdtype", @@ -78,9 +75,6 @@ ohos_static_library("rawtrace_parser") { if (!is_independent_compile) { configs = [ "${TS_DIR}/gn:ts_config" ] } - if (is_mingw) { - cflags = [ "-includeMingW64Fix.h" ] - } public_deps = [ "${OHOS_TRACE_STREAMER_PROTOS_DIR}/protos/services:ts_all_type_cpp_standard", diff --git a/trace_streamer/src/parser/rawtrace_parser/cpu_detail_parser.cpp b/trace_streamer/src/parser/rawtrace_parser/cpu_detail_parser.cpp index 998bed996..9c0f3546a 100644 --- a/trace_streamer/src/parser/rawtrace_parser/cpu_detail_parser.cpp +++ b/trace_streamer/src/parser/rawtrace_parser/cpu_detail_parser.cpp @@ -86,6 +86,14 @@ void CpuDetailParser::InterruptEventInitialization() std::bind(&CpuDetailParser::SoftIrqEntryEvent, this, std::placeholders::_1)); eventToFunctionMap_.emplace(config_.eventNameMap_.at(TRACE_EVENT_SOFTIRQ_EXIT), std::bind(&CpuDetailParser::SoftIrqExitEvent, this, std::placeholders::_1)); + eventToFunctionMap_.emplace(config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_INIT), + std::bind(&CpuDetailParser::DmaFenceInitEvent, this, std::placeholders::_1)); + eventToFunctionMap_.emplace(config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_DESTROY), + std::bind(&CpuDetailParser::DmaFenceDestroyEvent, this, std::placeholders::_1)); + eventToFunctionMap_.emplace(config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_ENABLE), + std::bind(&CpuDetailParser::DmaFenceEnableEvent, this, std::placeholders::_1)); + eventToFunctionMap_.emplace(config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_SIGNALED), + std::bind(&CpuDetailParser::DmaFenceSignaledEvent, this, std::placeholders::_1)); } void CpuDetailParser::ClockEventInitialization() { @@ -594,6 +602,62 @@ bool CpuDetailParser::SoftIrqExitEvent(const RawTraceEventInfo &event) const static_cast(softIrqExitMsg.vec())); return true; } +bool CpuDetailParser::DmaFenceInitEvent(const RawTraceEventInfo &event) const +{ + auto dmaFenceInitMsg = event.msgPtr->dma_fence_init_format(); + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE_INIT, STAT_EVENT_RECEIVED); + DmaFenceRow dmaFenceRow = {event.msgPtr->timestamp(), + 0, + dmaFenceInitIndex_, + traceDataCache_->GetDataIndex(dmaFenceInitMsg.driver()), + traceDataCache_->GetDataIndex(dmaFenceInitMsg.timeline()), + static_cast(dmaFenceInitMsg.context()), + static_cast(dmaFenceInitMsg.seqno())}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} +bool CpuDetailParser::DmaFenceDestroyEvent(const RawTraceEventInfo &event) const +{ + auto dmaFenceDestroyMsg = event.msgPtr->dma_fence_destroy_format(); + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE_DESTROY, STAT_EVENT_RECEIVED); + DmaFenceRow dmaFenceRow = {event.msgPtr->timestamp(), + 0, + dmaFenceDestroyIndex_, + traceDataCache_->GetDataIndex(dmaFenceDestroyMsg.driver()), + traceDataCache_->GetDataIndex(dmaFenceDestroyMsg.timeline()), + static_cast(dmaFenceDestroyMsg.context()), + static_cast(dmaFenceDestroyMsg.seqno())}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} +bool CpuDetailParser::DmaFenceEnableEvent(const RawTraceEventInfo &event) const +{ + auto dmaFenceEnableMsg = event.msgPtr->dma_fence_enable_signal_format(); + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE_ENABLE, STAT_EVENT_RECEIVED); + DmaFenceRow dmaFenceRow = {event.msgPtr->timestamp(), + 0, + dmaFenceEnableIndex_, + traceDataCache_->GetDataIndex(dmaFenceEnableMsg.driver()), + traceDataCache_->GetDataIndex(dmaFenceEnableMsg.timeline()), + static_cast(dmaFenceEnableMsg.context()), + static_cast(dmaFenceEnableMsg.seqno())}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} +bool CpuDetailParser::DmaFenceSignaledEvent(const RawTraceEventInfo &event) const +{ + auto dmaFenceSignaledMsg = event.msgPtr->dma_fence_signaled_format(); + traceDataCache_->GetStatAndInfo()->IncreaseStat(TRACE_EVENT_DMA_FENCE_SIGNALED, STAT_EVENT_RECEIVED); + DmaFenceRow dmaFenceRow = {event.msgPtr->timestamp(), + 0, + dmaFenceSignaledIndex_, + traceDataCache_->GetDataIndex(dmaFenceSignaledMsg.driver()), + traceDataCache_->GetDataIndex(dmaFenceSignaledMsg.timeline()), + static_cast(dmaFenceSignaledMsg.context()), + static_cast(dmaFenceSignaledMsg.seqno())}; + streamFilters_->sliceFilter_->DmaFence(dmaFenceRow); + return true; +} bool CpuDetailParser::SetRateEvent(const RawTraceEventInfo &event) const { auto clockSetRateMsg = event.msgPtr->clock_set_rate_format(); diff --git a/trace_streamer/src/parser/rawtrace_parser/cpu_detail_parser.h b/trace_streamer/src/parser/rawtrace_parser/cpu_detail_parser.h index 671b99a61..c796826cd 100644 --- a/trace_streamer/src/parser/rawtrace_parser/cpu_detail_parser.h +++ b/trace_streamer/src/parser/rawtrace_parser/cpu_detail_parser.h @@ -71,6 +71,10 @@ private: bool SoftIrqEntryEvent(const RawTraceEventInfo &event) const; bool SoftIrqRaiseEvent(const RawTraceEventInfo &event) const; bool SoftIrqExitEvent(const RawTraceEventInfo &event) const; + bool DmaFenceInitEvent(const RawTraceEventInfo &event) const; + bool DmaFenceDestroyEvent(const RawTraceEventInfo &event) const; + bool DmaFenceEnableEvent(const RawTraceEventInfo &event) const; + bool DmaFenceSignaledEvent(const RawTraceEventInfo &event) const; bool SetRateEvent(const RawTraceEventInfo &event) const; bool ClockEnableEvent(const RawTraceEventInfo &event) const; bool ClockDisableEvent(const RawTraceEventInfo &event) const; @@ -115,6 +119,10 @@ private: const DataIndex cpuFrequencyLimitMaxIndex_ = traceDataCache_->GetDataIndex("cpu_frequency_limits_max"); const DataIndex cpuFrequencyLimitMinIndex_ = traceDataCache_->GetDataIndex("cpu_frequency_limits_min"); const DataIndex workQueueIndex_ = traceDataCache_->GetDataIndex("workqueue"); + const DataIndex dmaFenceInitIndex_ = traceDataCache_->GetDataIndex("dma_fence_init"); + const DataIndex dmaFenceDestroyIndex_ = traceDataCache_->GetDataIndex("dma_fence_destroy"); + const DataIndex dmaFenceEnableIndex_ = traceDataCache_->GetDataIndex("dma_fence_enable_signal"); + const DataIndex dmaFenceSignaledIndex_ = traceDataCache_->GetDataIndex("dma_fence_signaled"); }; } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.cpp b/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.cpp index 326586f37..0dded97b8 100644 --- a/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.cpp +++ b/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.cpp @@ -96,6 +96,21 @@ void FtraceEventProcessor::InterruptEventInitialization() eventNameToFunctions_.emplace(config_.eventNameMap_.at(TRACE_EVENT_SOFTIRQ_EXIT), std::bind(&FtraceEventProcessor::SoftirqExit, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + eventNameToFunctions_.emplace(config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_INIT), + std::bind(&FtraceEventProcessor::DmaFenceInit, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + + eventNameToFunctions_.emplace(config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_DESTROY), + std::bind(&FtraceEventProcessor::DmaFenceDestroy, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + + eventNameToFunctions_.emplace(config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_ENABLE), + std::bind(&FtraceEventProcessor::DmaFenceEnable, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + + eventNameToFunctions_.emplace(config_.eventNameMap_.at(TRACE_EVENT_DMA_FENCE_SIGNALED), + std::bind(&FtraceEventProcessor::DmaFenceSignaled, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); } void FtraceEventProcessor::ClockEventInitialization() { @@ -551,6 +566,59 @@ bool FtraceEventProcessor::SoftirqExit(FtraceEvent &ftraceEvent, uint8_t data[], softirqExitMsg->set_vec(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); return true; } +bool FtraceEventProcessor::DmaFenceInit(FtraceEvent &ftraceEvent, + uint8_t data[], + size_t size, + const EventFormat &format) +{ + uint8_t index = 0; + auto dmaFenceInitMsg = ftraceEvent.mutable_dma_fence_init_format(); + dmaFenceInitMsg->set_driver(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + dmaFenceInitMsg->set_timeline(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + dmaFenceInitMsg->set_context(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); + dmaFenceInitMsg->set_seqno(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); + return true; +} +bool FtraceEventProcessor::DmaFenceDestroy(FtraceEvent &ftraceEvent, + uint8_t data[], + size_t size, + const EventFormat &format) +{ + uint8_t index = 0; + auto dmaFenceDestroyMsg = ftraceEvent.mutable_dma_fence_destroy_format(); + dmaFenceDestroyMsg->set_driver(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + dmaFenceDestroyMsg->set_timeline(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + dmaFenceDestroyMsg->set_context(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); + dmaFenceDestroyMsg->set_seqno(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); + return true; +} +bool FtraceEventProcessor::DmaFenceEnable(FtraceEvent &ftraceEvent, + uint8_t data[], + size_t size, + const EventFormat &format) +{ + uint8_t index = 0; + auto dmaFenceEnableMsg = ftraceEvent.mutable_dma_fence_enable_signal_format(); + dmaFenceEnableMsg->set_driver(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + dmaFenceEnableMsg->set_timeline(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + dmaFenceEnableMsg->set_context(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); + dmaFenceEnableMsg->set_seqno(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); + return true; +} +bool FtraceEventProcessor::DmaFenceSignaled(FtraceEvent &ftraceEvent, + uint8_t data[], + size_t size, + const EventFormat &format) +{ + uint8_t index = 0; + auto dmaFenceSignaledMsg = ftraceEvent.mutable_dma_fence_signaled_format(); + dmaFenceSignaledMsg->set_driver(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + dmaFenceSignaledMsg->set_timeline(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + dmaFenceSignaledMsg->set_context( + FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); + dmaFenceSignaledMsg->set_seqno(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); + return true; +} bool FtraceEventProcessor::ClockSetRate(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, diff --git a/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.h b/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.h index 5b159d116..9d95feb7f 100644 --- a/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.h +++ b/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.h @@ -43,6 +43,10 @@ private: bool SoftirqRaise(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); bool SoftirqEntry(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); bool SoftirqExit(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); + bool DmaFenceInit(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); + bool DmaFenceDestroy(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); + bool DmaFenceEnable(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); + bool DmaFenceSignaled(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); bool SuspendResume(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); bool WorkqueueExecuteStart(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); bool WorkqueueExecuteEnd(FtraceEvent &ftraceEvent, uint8_t data[], size_t size, const EventFormat &format); diff --git a/trace_streamer/src/rpc/ffrt_converter.cpp b/trace_streamer/src/rpc/ffrt_converter.cpp index c228338d6..55cb03c43 100644 --- a/trace_streamer/src/rpc/ffrt_converter.cpp +++ b/trace_streamer/src/rpc/ffrt_converter.cpp @@ -142,7 +142,8 @@ std::string FfrtConverter::ReplaceSchedSwitchLog(std::string &fakeLog, std::smatch match; auto taskId = GetTaskId(pid, gid); if (mark.find("prev_pid=" + std::to_string(tid)) != std::string::npos) { - if (regex_search(fakeLog, match, indexPattern_)) { + if (regex_search(fakeLog, match, indexPattern_) && fakeLog.find("prev_comm=") != std::string::npos && + fakeLog.find("prev_prio=") != std::string::npos) { auto beginPos = fakeLog.find(match.str()); (void)sprintf_s(result.get(), MAX_LEN, " %s-%s ", taskLabels_[pid][gid].c_str(), taskId.c_str()); fakeLog = result.get() + fakeLog.substr(beginPos); @@ -158,7 +159,8 @@ std::string FfrtConverter::ReplaceSchedSwitchLog(std::string &fakeLog, fakeLog = fakeLog.substr(0, pPidPos) + result.get() + fakeLog.substr(pPrioPos); memset_s(result.get(), MAX_LEN, 0, MAX_LEN); } - } else if (mark.find("next_pid=" + std::to_string(tid)) != std::string::npos) { + } else if (mark.find("next_pid=" + std::to_string(tid)) != std::string::npos && + fakeLog.find("next_comm=") != std::string::npos && fakeLog.find("next_prio=") != std::string::npos) { (void)sprintf_s(result.get(), MAX_LEN, "next_comm=%s ", taskLabels_[pid][gid].c_str()); size_t nCommPos = fakeLog.find("next_comm="); size_t nPidPos = fakeLog.find("next_pid="); @@ -182,12 +184,16 @@ std::string FfrtConverter::ReplaceSchedWakeLog(std::string &fakeLog, (void)sprintf_s(result.get(), MAX_LEN, "comm=%s ", label.c_str()); size_t commPos = fakeLog.find("comm="); size_t pidPos = fakeLog.find("pid="); - fakeLog = fakeLog.substr(0, commPos) + result.get() + fakeLog.substr(pidPos); - memset_s(result.get(), MAX_LEN, 0, MAX_LEN); - (void)sprintf_s(result.get(), MAX_LEN, "pid=%s ", taskId.c_str()); - pidPos = fakeLog.find("pid="); - size_t prioPos = fakeLog.find("prio="); - fakeLog = fakeLog.substr(0, pidPos) + result.get() + fakeLog.substr(prioPos); + if (commPos != std::string::npos && pidPos != std::string::npos) { + fakeLog = fakeLog.substr(0, commPos) + result.get() + fakeLog.substr(pidPos); + memset_s(result.get(), MAX_LEN, 0, MAX_LEN); + (void)sprintf_s(result.get(), MAX_LEN, "pid=%s ", taskId.c_str()); + pidPos = fakeLog.find("pid="); + size_t prioPos = fakeLog.find("prio="); + if (prioPos != std::string::npos) { + fakeLog = fakeLog.substr(0, pidPos) + result.get() + fakeLog.substr(prioPos); + } + } return fakeLog; } @@ -198,7 +204,9 @@ std::string FfrtConverter::ReplaceSchedBlockLog(std::string &fakeLog, const int (void)sprintf_s(result.get(), MAX_LEN, "pid=%s ", taskId.c_str()); size_t pidPos = fakeLog.find("pid"); size_t ioPos = fakeLog.find("iowait="); - fakeLog = fakeLog.substr(0, pidPos) + result.get() + fakeLog.substr(ioPos); + if (pidPos != std::string::npos && ioPos != std::string::npos) { + fakeLog = fakeLog.substr(0, pidPos) + result.get() + fakeLog.substr(ioPos); + } return fakeLog; } std::string FfrtConverter::ReplaceTracingMarkLog(std::string &fakeLog, @@ -235,9 +243,14 @@ std::string FfrtConverter::ConvertWorkerLogToTask(const std::string &mark, } int FfrtConverter::FindIntNumberAfterStr(const size_t index, const string &str) { - auto beginPos = context_[index].find(str) + str.length(); - auto endPos = context_[index].find_first_of(" ", beginPos); - return stoi(context_[index].substr(beginPos, endPos - beginPos)); + + auto beginPos = context_[index].find(str); + if (beginPos != std::string::npos) { + beginPos = beginPos + str.length(); + auto endPos = context_[index].find_first_of(" ", beginPos); + return stoi(context_[index].substr(beginPos, endPos - beginPos)); + } + return -1; } std::string FfrtConverter::FindSubStrAfterStr(const size_t index, const string &str) { @@ -250,10 +263,10 @@ void FfrtConverter::ClassifySchedSwitchData(const size_t index, std::unordered_m auto prevTid = FindIntNumberAfterStr(index, "prev_pid="); auto nextTid = FindIntNumberAfterStr(index, "next_pid="); // update sched_switch prev_pid and next_pid corresponding line number - if (traceMap.count(prevTid) == 0) { + if (prevTid != -1 && traceMap.count(prevTid) == 0) { traceMap[prevTid] = std::vector(); } - if (traceMap.count(nextTid) == 0) { + if (nextTid != -1 && traceMap.count(nextTid) == 0) { traceMap[nextTid] = std::vector(); } traceMap[prevTid].push_back(index); @@ -290,12 +303,14 @@ void FfrtConverter::FindFfrtProcessAndClassify(const size_t index, std::unordere std::string res = context_[index].substr(0, endPos); std::string begin = "-"; auto beginPos = res.find_last_of(begin); - beginPos = beginPos + begin.length(); - auto tid = stoi(context_[index].substr(beginPos, endPos - beginPos)); - if (traceMap.find(tid) == traceMap.end()) { - traceMap[tid] = std::vector(); + if (beginPos != std::string::npos) { + beginPos = beginPos + begin.length(); + auto tid = stoi(context_[index].substr(beginPos, endPos - beginPos)); + if (traceMap.find(tid) == traceMap.end()) { + traceMap[tid] = std::vector(); + } + traceMap[tid].push_back(index); } - traceMap[tid].push_back(index); } return; } @@ -394,14 +409,18 @@ void FfrtConverter::ProcessMarkWithSchedSwitch(const int tid, int &prio, const s if (context_[index].find("prev_pid=" + std::to_string(tid) + " ") != std::string::npos) { static std::string beginPprio = "prev_prio="; auto beginPos = context_[index].find(beginPprio); - beginPos = beginPos + beginPprio.length(); - auto endPos = context_[index].find_first_of(" ", beginPos); - prio = stoi(context_[index].substr(beginPos, endPos - beginPos)); + if (beginPos != std::string::npos) { + beginPos = beginPos + beginPprio.length(); + auto endPos = context_[index].find_first_of(" ", beginPos); + prio = stoi(context_[index].substr(beginPos, endPos - beginPos)); + } } else if (context_[index].find("next_pid=" + std::to_string(tid)) != std::string::npos) { static std::string beginNprio = "next_prio="; auto beginPos = context_[index].find(beginNprio); - beginPos = beginPos + beginNprio.length(); - prio = stoi(context_[index].substr(beginPos)); + if (beginPos != std::string::npos) { + beginPos = beginPos + beginNprio.length(); + prio = stoi(context_[index].substr(beginPos)); + } } } } @@ -412,25 +431,33 @@ std::string FfrtConverter::GetLabel(const string &line) if (line.find("H:FFRT::") != std::string::npos) { auto beginPos = line.rfind("["); auto endPos = line.rfind("]"); - label = line.substr(beginPos + 1, endPos - beginPos - 1); + if (beginPos != std::string::npos && endPos != std::string::npos) { + label = line.substr(beginPos + 1, endPos - beginPos - 1); + } } else { static std::string indexHFfrt = "|H:FFRT"; auto beginPos = line.find(indexHFfrt); beginPos = beginPos + indexHFfrt.length(); auto endPos = line.find_first_of("|", beginPos); - label = line.substr(beginPos, endPos - beginPos); + if (endPos != std::string::npos) { + label = line.substr(beginPos, endPos - beginPos); + } } } else { if (line.find("|FFRT::") != std::string::npos) { auto beginPos = line.rfind("["); auto endPos = line.rfind("]"); - label = line.substr(beginPos + 1, endPos - beginPos - 1); + if (beginPos != std::string::npos && endPos != std::string::npos) { + label = line.substr(beginPos + 1, endPos - beginPos - 1); + } } else { static std::string indexFfrt = "|FFRT"; auto beginPos = line.find(indexFfrt); beginPos = beginPos + indexFfrt.length(); auto endPos = line.find_first_of("|", beginPos); - label = line.substr(beginPos, endPos - beginPos); + if (endPos != std::string::npos) { + label = line.substr(beginPos, endPos - beginPos); + } } } return label; diff --git a/trace_streamer/src/rpc/wasm_func.cpp b/trace_streamer/src/rpc/wasm_func.cpp index 0afeca2a6..031d2cdda 100644 --- a/trace_streamer/src/rpc/wasm_func.cpp +++ b/trace_streamer/src/rpc/wasm_func.cpp @@ -255,13 +255,6 @@ EMSCRIPTEN_KEEPALIVE int32_t TraceStreamerParseDataOver() } return -1; } -EMSCRIPTEN_KEEPALIVE int32_t TraceStreamerSqlOperate(const uint8_t *sql, int32_t sqlLen) -{ - if (g_wasmTraceStreamer.SqlOperate(sql, sqlLen, nullptr)) { - return 0; - } - return -1; -} EMSCRIPTEN_KEEPALIVE int32_t TraceStreamerSqlOperateEx(int32_t sqlLen) { if (g_wasmTraceStreamer.SqlOperate(g_reqBuf, sqlLen, nullptr)) { diff --git a/trace_streamer/src/rpc/wasm_func.h b/trace_streamer/src/rpc/wasm_func.h index 6fc0fa87f..b511f4f1d 100644 --- a/trace_streamer/src/rpc/wasm_func.h +++ b/trace_streamer/src/rpc/wasm_func.h @@ -38,7 +38,6 @@ extern "C" { int32_t TraceStreamerParseData(const uint8_t *data, int32_t dataLen); int32_t TraceStreamerParseDataEx(int32_t dataLen, bool isFinish); int32_t TraceStreamerParseDataOver(); -int32_t TraceStreamerSqlOperate(const uint8_t *sql, int32_t sqlLen); int32_t TraceStreamerSqlOperateEx(int32_t sqlLen); int32_t TraceStreamerReset(); int32_t TraceStreamerSqlQuery(const uint8_t *sql, int32_t sqlLen, uint8_t *out, int32_t outLen); diff --git a/trace_streamer/src/table/ftrace/BUILD.gn b/trace_streamer/src/table/ftrace/BUILD.gn index d43e07038..a68a10bc0 100644 --- a/trace_streamer/src/table/ftrace/BUILD.gn +++ b/trace_streamer/src/table/ftrace/BUILD.gn @@ -25,6 +25,7 @@ ohos_source_set("ftrace_tables") { "clock_event_filter_table.cpp", "clock_snapshot_table.cpp", "cpu_measure_filter_table.cpp", + "dma_fence_table.cpp", "dynamic_frame_table.cpp", "filter_table.cpp", "frame_maps_table.cpp", diff --git a/trace_streamer/src/table/ftrace/dma_fence_table.cpp b/trace_streamer/src/table/ftrace/dma_fence_table.cpp new file mode 100644 index 000000000..bffbae405 --- /dev/null +++ b/trace_streamer/src/table/ftrace/dma_fence_table.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dma_fence_table.h" + +namespace SysTuning { +namespace TraceStreamer { +enum class Index : int32_t { ID = 0, TS, DURS, CAT, DRIVER, TIMELINE, CONTEXT, SEQNO }; +DmaFenceTable::DmaFenceTable(const TraceDataCache *dataCache) : TableBase(dataCache) +{ + tableColumn_.emplace_back(TableBase::ColumnInfo("id", "INTEGER")); + tableColumn_.emplace_back(TableBase::ColumnInfo("ts", "INTEGER")); + tableColumn_.emplace_back(TableBase::ColumnInfo("dur", "INTEGER")); + tableColumn_.emplace_back(TableBase::ColumnInfo("cat", "TEXT")); + tableColumn_.emplace_back(TableBase::ColumnInfo("driver", "TEXT")); + tableColumn_.emplace_back(TableBase::ColumnInfo("timeline", "TEXT")); + tableColumn_.emplace_back(TableBase::ColumnInfo("context", "INTEGER")); + tableColumn_.emplace_back(TableBase::ColumnInfo("seqno", "INTEGER")); + tablePriKey_.push_back("id"); +} + +DmaFenceTable::~DmaFenceTable() {} + +void DmaFenceTable::FilterByConstraint(FilterConstraints &dmaFencefc, + double &dmaFencefilterCost, + size_t dmaFencerowCount, + uint32_t dmaFencecurrenti) +{ + const auto &dmaFencec = dmaFencefc.GetConstraints()[dmaFencecurrenti]; + switch (static_cast(dmaFencec.col)) { + case Index::ID: { + if (CanFilterId(dmaFencec.op, dmaFencerowCount)) { + dmaFencefc.UpdateConstraint(dmaFencecurrenti, true); + dmaFencefilterCost += 1; // id can position by 1 step + } else { + dmaFencefilterCost += dmaFencerowCount; // scan all rows + } + break; + } + default: // other column + dmaFencefilterCost += dmaFencerowCount; // scan all rows + break; + } +} + +std::unique_ptr DmaFenceTable::CreateCursor() +{ + return std::make_unique(dataCache_, this); +} + +DmaFenceTable::Cursor::Cursor(const TraceDataCache *dataCache, TableBase *table) + : TableBase::Cursor(dataCache, table, static_cast(dataCache->GetConstDmaFenceData().Size())), + dmaFenceObj_(dataCache->GetConstDmaFenceData()) +{ +} + +DmaFenceTable::Cursor::~Cursor() {} + +int32_t DmaFenceTable::Cursor::Filter(const FilterConstraints &fc, sqlite3_value **argv) +{ + // reset indexMap_ + indexMap_ = std::make_unique(0, rowCount_); + + if (rowCount_ <= 0) { + return SQLITE_OK; + } + + auto DmaFenceTabCs = fc.GetConstraints(); + for (size_t i = 0; i < DmaFenceTabCs.size(); i++) { + const auto &c = DmaFenceTabCs[i]; + switch (static_cast(c.col)) { + case Index::ID: + FilterId(c.op, argv[i]); + break; + case Index::TS: + FilterTS(c.op, argv[i], dmaFenceObj_.TimelinesData()); + break; + default: + break; + } + } + + auto DmaFenceTabOrderbys = fc.GetOrderBys(); + for (auto i = DmaFenceTabOrderbys.size(); i > 0;) { + i--; + switch (static_cast(DmaFenceTabOrderbys[i].iColumn)) { + case Index::ID: + indexMap_->SortBy(DmaFenceTabOrderbys[i].desc); + break; + default: + break; + } + } + + return SQLITE_OK; +} + +int32_t DmaFenceTable::Cursor::Column(int32_t column) const +{ + switch (static_cast(column)) { + case Index::ID: + sqlite3_result_int64(context_, CurrentRow()); + break; + case Index::TS: + SetTypeColumnInt64(dmaFenceObj_.TimeStampData()[CurrentRow()], INVALID_UINT64); + break; + case Index::DURS: + SetTypeColumnInt64(dmaFenceObj_.DursData()[CurrentRow()], INVALID_UINT64); + break; + case Index::CAT: { + SetTypeColumnText(dmaFenceObj_.CatsData()[CurrentRow()], INVALID_UINT64); + break; + } + case Index::DRIVER: { + SetTypeColumnText(dmaFenceObj_.DriversData()[CurrentRow()], INVALID_UINT64); + break; + } + case Index::TIMELINE: { + SetTypeColumnText(dmaFenceObj_.TimelinesData()[CurrentRow()], INVALID_UINT64); + break; + } + case Index::CONTEXT: { + SetTypeColumnInt32(dmaFenceObj_.ContextsData()[CurrentRow()], INVALID_UINT32); + break; + } + case Index::SEQNO: { + SetTypeColumnInt32(dmaFenceObj_.SeqnosData()[CurrentRow()], INVALID_UINT32); + break; + } + default: + break; + } + return SQLITE_OK; +} + +void DmaFenceTable::GetOrbyes(FilterConstraints &dmaFencefc, EstimatedIndexInfo &dmaFenceei) +{ + auto dmaFenceorderbys = dmaFencefc.GetOrderBys(); + for (auto i = 0; i < dmaFenceorderbys.size(); i++) { + switch (static_cast(dmaFenceorderbys[i].iColumn)) { + case Index::ID: + break; + default: // other columns can be sorted by SQLite + dmaFenceei.isOrdered = false; + break; + } + } +} +} // namespace TraceStreamer +} // namespace SysTuning \ No newline at end of file diff --git a/trace_streamer/src/table/ftrace/include/dma_fence_table.h b/trace_streamer/src/table/ftrace/include/dma_fence_table.h new file mode 100644 index 000000000..39bec3482 --- /dev/null +++ b/trace_streamer/src/table/ftrace/include/dma_fence_table.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TRACE_STREAMER_DMA_FENCE_TABLE_H +#define TRACE_STREAMER_DMA_FENCE_TABLE_H + +#include "table_base.h" +#include "trace_data_cache.h" + +namespace SysTuning { +namespace TraceStreamer { +class DmaFenceTable : public TableBase { +public: + explicit DmaFenceTable(const TraceDataCache *dataCache); + ~DmaFenceTable() override; + std::unique_ptr CreateCursor() override; + +private: + int64_t GetSize() override + { + return dataCache_->GetConstDmaFenceData().Size(); + } + void GetOrbyes(FilterConstraints &dmaFencefc, EstimatedIndexInfo &dmaFenceei) override; + void FilterByConstraint(FilterConstraints &dmaFencefc, + double &dmaFencefilterCost, + size_t dmaFencerowCount, + uint32_t dmaFencecurrenti) override; + class Cursor : public TableBase::Cursor { + public: + explicit Cursor(const TraceDataCache *dataCache, TableBase *table); + ~Cursor() override; + int32_t Filter(const FilterConstraints &fc, sqlite3_value **argv) override; + int32_t Column(int32_t column) const override; + + private: + const DmaFence &dmaFenceObj_; + }; +}; +} // namespace TraceStreamer +} // namespace SysTuning +#endif // TRACE_STREAMER_DMA_FENCE_TABLE_H diff --git a/trace_streamer/src/table/hiperf/BUILD.gn b/trace_streamer/src/table/hiperf/BUILD.gn index 56791a2b0..73c303dea 100644 --- a/trace_streamer/src/table/hiperf/BUILD.gn +++ b/trace_streamer/src/table/hiperf/BUILD.gn @@ -20,6 +20,7 @@ ohos_source_set("hiperf_tables") { sources = [ "perf_call_chain_table.cpp", "perf_files_table.cpp", + "perf_napi_async_table.cpp", "perf_report_table.cpp", "perf_sample_table.cpp", "perf_thread_table.cpp", diff --git a/trace_streamer/prebuilts/patch_hiperf/file_ex.h b/trace_streamer/src/table/hiperf/include/perf_napi_async_table.h similarity index 33% rename from trace_streamer/prebuilts/patch_hiperf/file_ex.h rename to trace_streamer/src/table/hiperf/include/perf_napi_async_table.h index 978663d8f..5c38d18c4 100644 --- a/trace_streamer/prebuilts/patch_hiperf/file_ex.h +++ b/trace_streamer/src/table/hiperf/include/perf_napi_async_table.h @@ -13,21 +13,42 @@ * limitations under the License. */ -#ifndef UTILS_BASE_FILE_EX_H -#define UTILS_BASE_FILE_EX_H +#ifndef PERF_NAPI_ASYNC_TABLE_H +#define PERF_NAPI_ASYNC_TABLE_H -#include -#include -namespace OHOS { -bool LoadStringFromFile(const std::string &filePath, std::string &content); -bool SaveStringToFile(const std::string &filePath, const std::string &content, bool truncated = true); -bool LoadStringFromFd(int fd, std::string &content); -bool SaveStringToFd(int fd, const std::string &content); -bool LoadBufferFromFile(const std::string &filePath, std::vector &content); -bool SaveBufferToFile(const std::string &filePath, const std::vector &content, bool truncated = true); -bool FileExists(const std::string &fileName); -bool StringExistsInFile(const std::string &fileName, const std::string &subStr, bool caseSensitive = true); -int CountStrInFile(const std::string &fileName, const std::string &subStr, bool caseSensitive = true); -} // namespace OHOS +#include "table_base.h" +#include "trace_data_cache.h" -#endif +namespace SysTuning { +namespace TraceStreamer { +class PerfNapiAsyncTable : public TableBase { +public: + explicit PerfNapiAsyncTable(const TraceDataCache *dataCache); + ~PerfNapiAsyncTable() override; + std::unique_ptr CreateCursor() override; + +private: + int64_t GetSize() override + { + return dataCache_->GetConstPerfNapiAsyncData().Size(); + } + void GetOrbyes(FilterConstraints &napiAsyncFc, EstimatedIndexInfo &napiAsyncEi) override; + void FilterByConstraint(FilterConstraints &chainfc, + double &chainfilterCost, + size_t chainrowCount, + uint32_t chaincurrenti) override; + + class Cursor : public TableBase::Cursor { + public: + explicit Cursor(const TraceDataCache *dataCache, TableBase *table); + ~Cursor() override; + int32_t Filter(const FilterConstraints &fc, sqlite3_value **argv) override; + int32_t Column(int32_t column) const override; + + private: + const PerfNapiAsync &perfNapiAsyncObj_; + }; +}; +} // namespace TraceStreamer +} // namespace SysTuning +#endif // PERF_NAPI_ASYNC_TABLE_H diff --git a/trace_streamer/src/table/hiperf/perf_napi_async_table.cpp b/trace_streamer/src/table/hiperf/perf_napi_async_table.cpp new file mode 100644 index 000000000..09ba3c58c --- /dev/null +++ b/trace_streamer/src/table/hiperf/perf_napi_async_table.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "perf_napi_async_table.h" +#include "ts_common.h" + +namespace SysTuning { +namespace TraceStreamer { +enum class Index : int32_t { + ID = 0, + TS, + TRACEID, + CPU_ID, + THREAD_ID, + PROCESS_ID, + CALLER_CALLCHAINID, + CALLEE_CALLCHAINID, + PERF_SAMPLE_ID, + EVENT_COUNT, + EVENT_TYPE_ID, +}; +PerfNapiAsyncTable::PerfNapiAsyncTable(const TraceDataCache *dataCache) : TableBase(dataCache) +{ + tableColumn_.emplace_back("id", "INTEGER"); + tableColumn_.emplace_back("ts", "INTEGER"); + tableColumn_.emplace_back("traceid", "TEXT"); + tableColumn_.emplace_back("cpu_id", "INTEGER"); + tableColumn_.emplace_back("thread_id", "INTEGER"); + tableColumn_.emplace_back("process_id", "INTEGER"); + tableColumn_.emplace_back("caller_callchainid", "INTEGER"); + tableColumn_.emplace_back("callee_callchainid", "INTEGER"); + tableColumn_.emplace_back("perf_sample_id", "INTEGER"); + tableColumn_.emplace_back("event_count", "INTEGER"); + tableColumn_.emplace_back("event_type_id", "INTEGER"); + tablePriKey_.emplace_back("id"); +} + +PerfNapiAsyncTable::~PerfNapiAsyncTable() {} + +void PerfNapiAsyncTable::FilterByConstraint(FilterConstraints &napiAsyncFc, + double &napiAsyncFilterCost, + size_t napiAsyncRowCount, + uint32_t napiAsyncCurrenti) +{ + // To use the EstimateFilterCost function in the TableBase parent class function to calculate the i-value of each + // for loop + const auto &napiAsyncC = napiAsyncFc.GetConstraints()[napiAsyncCurrenti]; + switch (static_cast(napiAsyncC.col)) { + case Index::ID: { + if (CanFilterId(napiAsyncC.op, napiAsyncRowCount)) { + napiAsyncFc.UpdateConstraint(napiAsyncCurrenti, true); + napiAsyncFilterCost += 1; // id can position by 1 step + } else { + napiAsyncFilterCost += napiAsyncRowCount; // scan all rows + } + break; + } + default: // other column + napiAsyncFilterCost += napiAsyncRowCount; // scan all rows + break; + } +} + +std::unique_ptr PerfNapiAsyncTable::CreateCursor() +{ + return std::make_unique(dataCache_, this); +} + +PerfNapiAsyncTable::Cursor::Cursor(const TraceDataCache *dataCache, TableBase *table) + : TableBase::Cursor(dataCache, table, static_cast(dataCache->GetConstPerfNapiAsyncData().Size())), + perfNapiAsyncObj_(dataCache->GetConstPerfNapiAsyncData()) +{ +} + +PerfNapiAsyncTable::Cursor::~Cursor() {} + +int32_t PerfNapiAsyncTable::Cursor::Filter(const FilterConstraints &fc, sqlite3_value **argv) +{ + // reset indexMap_ + indexMap_ = std::make_unique(0, rowCount_); + + if (rowCount_ <= 0) { + return SQLITE_OK; + } + + auto perfNapiAsyncCs = fc.GetConstraints(); + std::set sId = {static_cast(Index::ID)}; + SwapIndexFront(perfNapiAsyncCs, sId); + for (size_t i = 0; i < perfNapiAsyncCs.size(); i++) { + const auto &c = perfNapiAsyncCs[i]; + switch (static_cast(c.col)) { + case Index::ID: + FilterId(c.op, argv[c.idxInaConstraint]); + break; + case Index::TRACEID: + indexMap_->MixRange(c.op, static_cast(sqlite3_value_int64(argv[c.idxInaConstraint])), + perfNapiAsyncObj_.Traceids()); + break; + case Index::THREAD_ID: + indexMap_->MixRange(c.op, static_cast(sqlite3_value_int64(argv[c.idxInaConstraint])), + perfNapiAsyncObj_.InternalTidsData()); + break; + case Index::CALLER_CALLCHAINID: + indexMap_->MixRange(c.op, static_cast(sqlite3_value_int64(argv[c.idxInaConstraint])), + perfNapiAsyncObj_.CallerCallchainids()); + break; + case Index::CALLEE_CALLCHAINID: + indexMap_->MixRange(c.op, static_cast(sqlite3_value_int64(argv[c.idxInaConstraint])), + perfNapiAsyncObj_.CalleeCallchainids()); + break; + default: + break; + } + } + + auto perfNapiAsyncOrderbys = fc.GetOrderBys(); + for (auto i = perfNapiAsyncOrderbys.size(); i > 0;) { + i--; + switch (static_cast(perfNapiAsyncOrderbys[i].iColumn)) { + case Index::ID: + indexMap_->SortBy(perfNapiAsyncOrderbys[i].desc); + break; + default: + break; + } + } + + return SQLITE_OK; +} + +int32_t PerfNapiAsyncTable::Cursor::Column(int32_t column) const +{ + switch (static_cast(column)) { + case Index::ID: + sqlite3_result_int64(context_, perfNapiAsyncObj_.IdsData()[CurrentRow()]); + break; + case Index::TS: + SetTypeColumnInt64(perfNapiAsyncObj_.TimeStampData()[CurrentRow()], INVALID_UINT64); + break; + case Index::TRACEID: + SetTypeColumnText(perfNapiAsyncObj_.Traceids()[CurrentRow()], INVALID_UINT64); + break; + case Index::CPU_ID: + SetTypeColumnInt64(perfNapiAsyncObj_.CpuIds()[CurrentRow()], INVALID_UINT64); + break; + case Index::THREAD_ID: + SetTypeColumnInt64(perfNapiAsyncObj_.InternalTidsData()[CurrentRow()], INVALID_UINT64); + break; + case Index::PROCESS_ID: + SetTypeColumnInt64(perfNapiAsyncObj_.ProcessIds()[CurrentRow()], INVALID_UINT64); + break; + case Index::CALLER_CALLCHAINID: + SetTypeColumnInt64(perfNapiAsyncObj_.CallerCallchainids()[CurrentRow()], INVALID_UINT64); + break; + case Index::CALLEE_CALLCHAINID: + SetTypeColumnInt64(perfNapiAsyncObj_.CalleeCallchainids()[CurrentRow()], INVALID_UINT64); + break; + case Index::PERF_SAMPLE_ID: + SetTypeColumnInt64(perfNapiAsyncObj_.PerfSampleIds()[CurrentRow()], INVALID_UINT64); + break; + case Index::EVENT_COUNT: + SetTypeColumnInt64(perfNapiAsyncObj_.EventCounts()[CurrentRow()], INVALID_UINT64); + break; + case Index::EVENT_TYPE_ID: + SetTypeColumnInt64(perfNapiAsyncObj_.EventTypeIds()[CurrentRow()], INVALID_UINT64); + break; + default: + TS_LOGF("Unregistered column : %d", column); + break; + } + return SQLITE_OK; +} + +void PerfNapiAsyncTable::GetOrbyes(FilterConstraints &napiAsyncFc, EstimatedIndexInfo &napiAsyncEi) +{ + auto napiAsyncOrderbys = napiAsyncFc.GetOrderBys(); + for (auto i = 0; i < napiAsyncOrderbys.size(); i++) { + switch (static_cast(napiAsyncOrderbys[i].iColumn)) { + case Index::ID: + break; + default: // other columns can be sorted by SQLite + napiAsyncEi.isOrdered = false; + break; + } + } +} +} // namespace TraceStreamer +} // namespace SysTuning diff --git a/trace_streamer/src/trace_data/trace_data_cache.cpp b/trace_streamer/src/trace_data/trace_data_cache.cpp index 7879c1c57..78b8a3b52 100644 --- a/trace_streamer/src/trace_data/trace_data_cache.cpp +++ b/trace_streamer/src/trace_data/trace_data_cache.cpp @@ -36,6 +36,7 @@ #include "datasource_clockid_table.h" #include "device_info_table.h" #include "device_state_table.h" +#include "dma_fence_table.h" #include "disk_io_table.h" #include "dynamic_frame_table.h" #include "ebpf_callstack_table.h" @@ -83,6 +84,7 @@ #include "perf_report_table.h" #include "perf_sample_table.h" #include "perf_thread_table.h" +#include "perf_napi_async_table.h" #include "process_measure_filter_table.h" #include "process_table.h" #include "range_table.h" @@ -151,6 +153,7 @@ void TraceDataCache::InitHiperfDB() TableBase::TableDeclare(*db_, this, "perf_callchain"); TableBase::TableDeclare(*db_, this, "perf_thread"); TableBase::TableDeclare(*db_, this, "perf_files"); + TableBase::TableDeclare(*db_, this, "perf_napi_async"); } void TraceDataCache::InitMeasureDB() { @@ -245,6 +248,7 @@ void TraceDataCache::InitDB() TableBase::TableDeclare(*db_, this, "diskio"); TableBase::TableDeclare(*db_, this, "cpu_usage"); TableBase::TableDeclare(*db_, this, "live_process"); + TableBase::TableDeclare(*db_, this, "dma_fence"); dbInited_ = true; } bool TraceDataCache::AnimationTraceEnabled() const diff --git a/trace_streamer/src/trace_data/trace_data_cache_base.h b/trace_streamer/src/trace_data/trace_data_cache_base.h index 8d16faaf4..b2dde69a8 100644 --- a/trace_streamer/src/trace_data/trace_data_cache_base.h +++ b/trace_streamer/src/trace_data/trace_data_cache_base.h @@ -133,6 +133,7 @@ public: PerfThread perfThread_; PerfFiles perfFiles_; PerfReport perfReport_; + PerfNapiAsync perfNapiAsync_; std::deque internalProcessesData_ = {}; std::deque internalThreadsData_ = {}; @@ -169,6 +170,7 @@ public: FrameSlice frameSliceData_; FrameMaps frameMapsData_; GPUSlice gpuSliceData_; + DmaFence dmaFenceData_; TaskPoolInfo taskPoolInfo_; JsHeapFiles jsHeapFilesData_; JsHeapEdges jsHeapEdgesData_; diff --git a/trace_streamer/src/trace_data/trace_data_cache_reader.cpp b/trace_streamer/src/trace_data/trace_data_cache_reader.cpp index cb12c7ad8..81d0c1b6e 100644 --- a/trace_streamer/src/trace_data/trace_data_cache_reader.cpp +++ b/trace_streamer/src/trace_data/trace_data_cache_reader.cpp @@ -171,6 +171,10 @@ const PerfReport &TraceDataCacheReader::GetConstPerfReportData() const { return perfReport_; } +const PerfNapiAsync &TraceDataCacheReader::GetConstPerfNapiAsyncData() const +{ + return perfNapiAsync_; +} const SysCall &TraceDataCacheReader::GetConstSysCallData() const { return sysCallData_; @@ -320,6 +324,10 @@ const GPUSlice &TraceDataCacheReader::GetConstGPUSliceData() const { return gpuSliceData_; } +const DmaFence &TraceDataCacheReader::GetConstDmaFenceData() const +{ + return dmaFenceData_; +} const AppStartup &TraceDataCacheReader::GetConstAppStartupData() const { return appStartupData_; diff --git a/trace_streamer/src/trace_data/trace_data_cache_reader.h b/trace_streamer/src/trace_data/trace_data_cache_reader.h index 5290b41f9..dacfcc2f5 100644 --- a/trace_streamer/src/trace_data/trace_data_cache_reader.h +++ b/trace_streamer/src/trace_data/trace_data_cache_reader.h @@ -82,6 +82,7 @@ public: const PerfSample &GetConstPerfSampleData() const; const PerfThread &GetConstPerfThreadData() const; const PerfReport &GetConstPerfReportData() const; + const PerfNapiAsync &GetConstPerfNapiAsyncData() const; const ArgSet &GetConstArgSetData() const; const DataType &GetConstDataTypeData() const; const SysMeasureFilter &GetConstSysMeasureFilterData() const; @@ -104,6 +105,7 @@ public: const FrameSlice &GetConstFrameSliceData() const; const FrameMaps &GetConstFrameMapsData() const; const GPUSlice &GetConstGPUSliceData() const; + const DmaFence &GetConstDmaFenceData() const; const TaskPoolInfo &GetConstTaskPoolData() const; const JsHeapFiles &GetConstJsHeapFilesData() const; const JsHeapEdges &GetConstJsHeapEdgesData() const; diff --git a/trace_streamer/src/trace_data/trace_data_cache_writer.cpp b/trace_streamer/src/trace_data/trace_data_cache_writer.cpp index 261e66521..8439ab574 100644 --- a/trace_streamer/src/trace_data/trace_data_cache_writer.cpp +++ b/trace_streamer/src/trace_data/trace_data_cache_writer.cpp @@ -223,6 +223,10 @@ PerfReport *TraceDataCacheWriter::GetPerfReportData() { return &perfReport_; } +PerfNapiAsync *TraceDataCacheWriter::GetPerfNapiAsyncData() +{ + return &perfNapiAsync_; +} ArgSet *TraceDataCacheWriter::GetArgSetData() { return &argSet_; @@ -316,6 +320,10 @@ GPUSlice *TraceDataCacheWriter::GetGPUSliceData() { return &gpuSliceData_; } +DmaFence *TraceDataCacheWriter::GetDmaFenceData() +{ + return &dmaFenceData_; +} TaskPoolInfo *TraceDataCacheWriter::GetTaskPoolData() { return &taskPoolInfo_; @@ -515,6 +523,7 @@ void TraceDataCacheWriter::Clear() gpuProcessMemData_.Clear(); gpuWindowMemData_.Clear(); gpuSliceData_.Clear(); + dmaFenceData_.Clear(); frameMapsData_.Clear(); frameSliceData_.Clear(); } diff --git a/trace_streamer/src/trace_data/trace_data_cache_writer.h b/trace_streamer/src/trace_data/trace_data_cache_writer.h index 8dd2a02a6..d916002e8 100644 --- a/trace_streamer/src/trace_data/trace_data_cache_writer.h +++ b/trace_streamer/src/trace_data/trace_data_cache_writer.h @@ -66,6 +66,7 @@ public: PerfSample *GetPerfSampleData(); PerfThread *GetPerfThreadData(); PerfReport *GetPerfReportData(); + PerfNapiAsync *GetPerfNapiAsyncData(); ArgSet *GetArgSetData(); DataType *GetDataTypeData(); SysMeasureFilter *GetSysMeasureFilterData(); @@ -88,6 +89,7 @@ public: FrameSlice *GetFrameSliceData(); FrameMaps *GetFrameMapsData(); GPUSlice *GetGPUSliceData(); + DmaFence *GetDmaFenceData(); TaskPoolInfo *GetTaskPoolData(); JsHeapFiles *GetJsHeapFilesData(); JsHeapEdges *GetJsHeapEdgesData(); diff --git a/trace_streamer/src/trace_data/trace_stdtype/base_stdtype.h b/trace_streamer/src/trace_data/trace_stdtype/base_stdtype.h index c1babaa77..ceaf74807 100644 --- a/trace_streamer/src/trace_data/trace_stdtype/base_stdtype.h +++ b/trace_streamer/src/trace_data/trace_stdtype/base_stdtype.h @@ -69,7 +69,7 @@ public: } virtual void ClearExportedData() = 0; template - void EraseElements(T &deq, changedata &... args) + void EraseElements(T &deq, changedata &...args) { deq.erase(deq.begin(), deq.begin() + readySize_); EraseElements(args...); diff --git a/trace_streamer/src/trace_data/trace_stdtype/ftrace/render_service_stdtype.cpp b/trace_streamer/src/trace_data/trace_stdtype/ftrace/render_service_stdtype.cpp index c8253ab2f..aa5a6a06d 100644 --- a/trace_streamer/src/trace_data/trace_stdtype/ftrace/render_service_stdtype.cpp +++ b/trace_streamer/src/trace_data/trace_stdtype/ftrace/render_service_stdtype.cpp @@ -180,6 +180,47 @@ const std::deque &GPUSlice::Durs() const { return durs_; } +size_t DmaFence::AppendNew(const DmaFenceRow &dmaFenceRow) +{ + timeStamps_.emplace_back(dmaFenceRow.timeStamp); + durs_.emplace_back(dmaFenceRow.duration); + cats_.emplace_back(dmaFenceRow.eventName); + drivers_.emplace_back(dmaFenceRow.driver); + timelines_.emplace_back(dmaFenceRow.timeline); + contexts_.emplace_back(dmaFenceRow.context); + seqnos_.emplace_back(dmaFenceRow.seqno); + return Size() - 1; +} + +const std::deque &DmaFence::DursData() const +{ + return durs_; +} + +const std::deque &DmaFence::CatsData() const +{ + return cats_; +} + +const std::deque &DmaFence::DriversData() const +{ + return drivers_; +} + +const std::deque &DmaFence::TimelinesData() const +{ + return timelines_; +} + +const std::deque &DmaFence::ContextsData() const +{ + return contexts_; +} + +const std::deque &DmaFence::SeqnosData() const +{ + return seqnos_; +} size_t FrameMaps::AppendNew(FrameSlice *frameSlice, uint64_t src, uint64_t dst) { diff --git a/trace_streamer/src/trace_data/trace_stdtype/ftrace/render_service_stdtype.h b/trace_streamer/src/trace_data/trace_stdtype/ftrace/render_service_stdtype.h index f19706bd3..412b16f45 100644 --- a/trace_streamer/src/trace_data/trace_stdtype/ftrace/render_service_stdtype.h +++ b/trace_streamer/src/trace_data/trace_stdtype/ftrace/render_service_stdtype.h @@ -106,6 +106,44 @@ private: std::deque frameRows_ = {}; std::deque durs_ = {}; }; +struct DmaFenceRow { + uint64_t timeStamp = INVALID_UINT64; + uint64_t duration = INVALID_UINT64; + DataIndex eventName = INVALID_DATAINDEX; + DataIndex driver = INVALID_DATAINDEX; + DataIndex timeline = INVALID_DATAINDEX; + uint32_t context = INVALID_UINT32; + uint32_t seqno = INVALID_UINT32; +}; + +class DmaFence : public CacheBase { +public: + size_t AppendNew(const DmaFenceRow &dmaFenceRow); + const std::deque &DursData() const; + const std::deque &CatsData() const; + const std::deque &DriversData() const; + const std::deque &TimelinesData() const; + const std::deque &ContextsData() const; + const std::deque &SeqnosData() const; + void Clear() override + { + CacheBase::Clear(); + durs_.clear(); + cats_.clear(); + drivers_.clear(); + timelines_.clear(); + contexts_.clear(); + seqnos_.clear(); + } + +private: + std::deque durs_ = {}; + std::deque cats_ = {}; + std::deque drivers_ = {}; + std::deque timelines_ = {}; + std::deque contexts_ = {}; + std::deque seqnos_ = {}; +}; class FrameMaps : public CacheBase, public BatchCacheBase { public: diff --git a/trace_streamer/src/trace_data/trace_stdtype/hiperf/hiperf_stdtype.cpp b/trace_streamer/src/trace_data/trace_stdtype/hiperf/hiperf_stdtype.cpp index 22555340d..022a1e5b5 100644 --- a/trace_streamer/src/trace_data/trace_stdtype/hiperf/hiperf_stdtype.cpp +++ b/trace_streamer/src/trace_data/trace_stdtype/hiperf/hiperf_stdtype.cpp @@ -78,6 +78,14 @@ void PerfCallChain::UpdateSymbolId(size_t index, DataIndex symbolId) symbolIds_[index] = symbolId; } } +void PerfCallChain::UpdateSymbolRelatedData(size_t index, uint64_t vaddrInFile, uint64_t symbolId, DataIndex nameIndex) +{ + if (index < Size()) { + vaddrInFiles_[index] = vaddrInFile; + symbolIds_[index] = symbolId; + names_[index] = nameIndex; + } +} size_t PerfFiles::AppendNewPerfFiles(uint64_t fileIds, uint32_t serial, DataIndex symbols, DataIndex filePath) { ids_.emplace_back(Size()); @@ -105,6 +113,31 @@ const std::deque &PerfFiles::FilePaths() const return filePaths_; } +bool PerfFiles::EraseFileIdSameData(uint64_t fileId) +{ + uint64_t start = INVALID_UINT64; + uint64_t end = INVALID_UINT64; + for (auto row = 0; row < Size(); row++) { + if (fileIds_[row] == fileId) { + if (start == INVALID_UINT64) { + start = row; + end = row; + } else { + end = row; + } + } + } + end++; + if (start <= end && end < Size()) { + ids_.erase(ids_.begin() + start, ids_.begin() + end); + fileIds_.erase(fileIds_.begin() + start, fileIds_.begin() + end); + serials_.erase(serials_.begin() + start, serials_.begin() + end); + symbols_.erase(symbols_.begin() + start, symbols_.begin() + end); + filePaths_.erase(filePaths_.begin() + start, filePaths_.begin() + end); + return true; + } + return false; +} void PerfFiles::Clear() { CacheBase::Clear(); @@ -210,5 +243,63 @@ const std::deque &PerfReport::Values() const { return values_; } +size_t PerfNapiAsync::AppendNewPerfNapiAsync(const PerfNapiAsyncRow &perfNapiAsyncRow) +{ + ids_.emplace_back(Size()); + timeStamps_.emplace_back(perfNapiAsyncRow.timeStamp); + traceids_.emplace_back(perfNapiAsyncRow.traceid); + cpuIds_.emplace_back(perfNapiAsyncRow.cpuId); + internalTids_.emplace_back(perfNapiAsyncRow.threadId); + processIds_.emplace_back(perfNapiAsyncRow.processId); + callerCallchainids_.emplace_back(perfNapiAsyncRow.callerCallchainid); + calleeCallchainids_.emplace_back(perfNapiAsyncRow.calleeCallchainid); + perfSampleIds_.emplace_back(perfNapiAsyncRow.perfSampleId); + eventCounts_.emplace_back(perfNapiAsyncRow.eventCount); + eventTypeIds_.emplace_back(perfNapiAsyncRow.eventTypeId); + return Size() - 1; +} +const std::deque &PerfNapiAsync::Traceids() const +{ + return traceids_; +} +const std::deque &PerfNapiAsync::CpuIds() const +{ + return cpuIds_; +} +const std::deque &PerfNapiAsync::ProcessIds() const +{ + return processIds_; +} +const std::deque &PerfNapiAsync::CallerCallchainids() const +{ + return callerCallchainids_; +} +const std::deque &PerfNapiAsync::CalleeCallchainids() const +{ + return calleeCallchainids_; +} +const std::deque &PerfNapiAsync::PerfSampleIds() const +{ + return perfSampleIds_; +} +const std::deque &PerfNapiAsync::EventCounts() const +{ + return eventCounts_; +} +const std::deque &PerfNapiAsync::EventTypeIds() const +{ + return eventTypeIds_; +} +void PerfNapiAsync::Clear() +{ + CacheBase::Clear(); + traceids_.clear(); + processIds_.clear(); + callerCallchainids_.clear(); + calleeCallchainids_.clear(); + perfSampleIds_.clear(); + eventCounts_.clear(); + eventTypeIds_.clear(); +} } // namespace TraceStdtype } // namespace SysTuning diff --git a/trace_streamer/src/trace_data/trace_stdtype/hiperf/hiperf_stdtype.h b/trace_streamer/src/trace_data/trace_stdtype/hiperf/hiperf_stdtype.h index 115506a47..0224d6369 100644 --- a/trace_streamer/src/trace_data/trace_stdtype/hiperf/hiperf_stdtype.h +++ b/trace_streamer/src/trace_data/trace_stdtype/hiperf/hiperf_stdtype.h @@ -43,6 +43,7 @@ public: void SetName(uint64_t index, DataIndex name); void UpdateSymbolId(size_t index, DataIndex symbolId); void Clear() override; + void UpdateSymbolRelatedData(size_t index, uint64_t vaddrInFile, uint64_t symbolId, DataIndex nameIndex); private: std::deque callChainIds_ = {}; @@ -62,6 +63,7 @@ public: const std::deque &FilePaths() const; const std::deque &Serials() const; void Clear() override; + bool EraseFileIdSameData(uint64_t fileId); private: std::deque fileIds_ = {}; @@ -125,6 +127,43 @@ private: std::deque types_ = {}; std::deque values_ = {}; }; + +struct PerfNapiAsyncRow { + uint64_t timeStamp = INVALID_UINT64; + DataIndex traceid = INVALID_UINT64; + uint8_t cpuId = INVALID_UINT8; + InternalTid threadId = INVALID_UINT32; + uint32_t processId = INVALID_UINT32; + uint32_t callerCallchainid = INVALID_UINT32; + uint32_t calleeCallchainid = INVALID_UINT32; + uint64_t perfSampleId = INVALID_UINT64; + uint64_t eventCount = 0; + uint64_t eventTypeId = 0; +}; + +class PerfNapiAsync : public CacheBase { +public: + size_t AppendNewPerfNapiAsync(const PerfNapiAsyncRow &perfNapiAsyncRow); + const std::deque &Traceids() const; + const std::deque &CpuIds() const; + const std::deque &ProcessIds() const; + const std::deque &CallerCallchainids() const; + const std::deque &CalleeCallchainids() const; + const std::deque &PerfSampleIds() const; + const std::deque &EventCounts() const; + const std::deque &EventTypeIds() const; + void Clear() override; + +private: + std::deque traceids_ = {}; + std::deque cpuIds_ = {}; + std::deque processIds_ = {}; + std::deque callerCallchainids_ = {}; + std::deque calleeCallchainids_ = {}; + std::deque perfSampleIds_ = {}; + std::deque eventCounts_ = {}; + std::deque eventTypeIds_ = {}; +}; } // namespace TraceStdtype } // namespace SysTuning #endif // HIPERF_STDTYPE_H diff --git a/trace_streamer/test/BUILD.gn b/trace_streamer/test/BUILD.gn index e05211d4c..74ef66c2b 100644 --- a/trace_streamer/test/BUILD.gn +++ b/trace_streamer/test/BUILD.gn @@ -107,35 +107,32 @@ if (is_test) { [ "${EXTEND_SRC}/parser/pbreader_stream_parser:stream_extend_parser" ] } include_dirs = [ - "../src", - "../src/trace_data", - "../src/table", - "../src/table/base", - "../src/filter", - "../src/metrics", - "../src/base", - "../src/rpc", - "../src/metrics", - "../src/include", - "../src/trace_streamer", - "../src/parser/ptreader_parser", - "../src/parser/ptreader_parser/bytrace_parser", - "../src/parser", - "../src/cfg", - "../src/metrics", - "../src/parser/ebpf_parser", - "../src/parser/hiperf_parser", - "../src/parser/hiperf_parser/include", - "../src/proto_reader", - "../src/proto_reader/include", - "../src/table/base/include", + "${SRC}", + "${SRC}/trace_data", + "${SRC}/table", + "${SRC}/table/base", + "${SRC}/filter", + "${SRC}/metrics", + "${SRC}/base", + "${SRC}/rpc", + "${SRC}/metrics", + "${SRC}/include", + "${SRC}/trace_streamer", + "${SRC}/parser/ptreader_parser", + "${SRC}/parser/ptreader_parser/bytrace_parser", + "${SRC}/parser", + "${SRC}/cfg", + "${SRC}/metrics", + "${SRC}/parser/ebpf_parser", + "${SRC}/parser/hiperf_parser", + "${SRC}/parser/hiperf_parser/include", + "${SRC}/proto_reader", + "${SRC}/proto_reader/include", + "${SRC}/table/base/include", "..", "unittest/base", "${THIRD_PARTY}/googletest/googletest/include/gtest", "${THIRD_PARTY}/protobuf/src", - "${PERF_DIR}/hiperf/include", - "${PERF_DIR}/hiperf/include/nonlinux/linux", - "${PERF_DIR}/hiperf/include/nonlinux", "${THIRD_PARTY}/protobuf/src/google/protobuf", "${THIRD_PARTY}/json/single_include/nlohmann", "${THIRD_PARTY}/perf_include/libbpf", @@ -145,25 +142,14 @@ if (is_test) { "${THIRD_PARTY}/sqlite/include", "${THIRD_PARTY}/bounds_checking_function/include", "${THIRD_PARTY}/json/single_include/nlohmann", - "../src/parser/pbreader_parser", - "../src/parser/pbreader_parser/htrace_parser", - "../src/proto_reader", - "../src/proto_reader/include", + "${SRC}/parser/pbreader_parser", + "${SRC}/parser/pbreader_parser/htrace_parser", + "${SRC}/proto_reader", + "${SRC}/proto_reader/include", "${SRC}/metrics", "${SRC}/parser/rawtrace_parser", ] - include_dirs += [ - "${THIRD_PARTY}/perf_include/hiviewdfx/hilog/include", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/cutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxlog", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/common/dfxutil", - "${THIRD_PARTY}/perf_include/hiviewdfx/hilog/include", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/common", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/nonlinux", - "${THIRD_PARTY}/perf_include/hiviewdfx/faultloggerd/interfaces/innerkits/unwinder/include", - ] - cflags = [ "-Wno-inconsistent-missing-override", "-Dprivate=public", #allow test code access private members diff --git a/trace_streamer/test/resource/Mmap.htrace b/trace_streamer/test/resource/Mmap.htrace old mode 100755 new mode 100644 diff --git a/trace_streamer/test/resource/ebpf_bio.htrace b/trace_streamer/test/resource/ebpf_bio.htrace old mode 100755 new mode 100644 diff --git a/trace_streamer/test/resource/hiprofiler_data_ability.htrace b/trace_streamer/test/resource/hiprofiler_data_ability.htrace old mode 100755 new mode 100644 diff --git a/trace_streamer/test/resource/hiprofiler_data_perf.htrace b/trace_streamer/test/resource/hiprofiler_data_perf.htrace old mode 100755 new mode 100644 diff --git a/trace_streamer/test/resource/htrace_perf_no_profiler_header.bin b/trace_streamer/test/resource/htrace_perf_no_profiler_header.bin old mode 100755 new mode 100644 diff --git a/trace_streamer/test/resource/perfCompressed.data b/trace_streamer/test/resource/perfCompressed.data old mode 100755 new mode 100644 -- Gitee From e1649cc66787f56f7063fc9280bdbadc143c6e90 Mon Sep 17 00:00:00 2001 From: jichuan Date: Tue, 18 Jun 2024 20:41:57 +0800 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=E5=91=8A=E8=AD=A6=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jichuan --- .gitignore | 1 + .../src/cfg/trace_streamer_config.cpp | 188 +++--------------- .../src/cfg/trace_streamer_config.h | 12 -- trace_streamer/src/filter/binder_filter.h | 4 +- trace_streamer/src/rpc/ffrt_converter.cpp | 1 - 5 files changed, 28 insertions(+), 178 deletions(-) diff --git a/.gitignore b/.gitignore index c5a81c0d9..2a8966244 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ trace_streamer/prebuilts/emsdk trace_streamer/prebuilts/linux trace_streamer/third_party trace_streamer/compile_commands.json +ts_tmp.perf.data trace_streamer/.cache tmp_* build-* diff --git a/trace_streamer/src/cfg/trace_streamer_config.cpp b/trace_streamer/src/cfg/trace_streamer_config.cpp index 862ff0d3d..f1718c6ab 100644 --- a/trace_streamer/src/cfg/trace_streamer_config.cpp +++ b/trace_streamer/src/cfg/trace_streamer_config.cpp @@ -99,7 +99,7 @@ void TraceStreamerConfig::PrintInfo() const printf("\n"); } -inline void TraceStreamerConfig::InitBinderEventNameMap() +void TraceStreamerConfig::InitBinderEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION, TRACE_ACTION_BINDER_TRANSACTION); eventNameMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION_RECEIVED, TRACE_ACTION_BINDER_TRANSACTION_RECEIVED); @@ -108,7 +108,7 @@ inline void TraceStreamerConfig::InitBinderEventNameMap() eventNameMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION_LOCKED, TRACE_ACTION_BINDER_TRANSACTION_LOCKED); eventNameMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION_UNLOCK, TRACE_ACTION_BINDER_TRANSACTION_UNLOCK); } -inline void TraceStreamerConfig::InitSchedEventNameMap() +void TraceStreamerConfig::InitSchedEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_SCHED_SWITCH, TRACE_ACTION_SCHED_SWITCH); eventNameMap_.emplace(TRACE_EVENT_SCHED_BLOCKED_REASON, TRACE_ACTION_SCHED_BLOCKED_REASON); @@ -116,7 +116,7 @@ inline void TraceStreamerConfig::InitSchedEventNameMap() eventNameMap_.emplace(TRACE_EVENT_SCHED_WAKING, TRACE_ACTION_SCHED_WAKING); eventNameMap_.emplace(TRACE_EVENT_SCHED_WAKEUP_NEW, TRACE_ACTION_SCHED_WAKEUP_NEW); } -inline void TraceStreamerConfig::InitClkEventNameMap() +void TraceStreamerConfig::InitClkEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_CLOCK_SET_RATE, TRACE_ACTION_CLOCK_SET_RATE); eventNameMap_.emplace(TRACE_EVENT_CLOCK_ENABLE, TRACE_ACTION_CLOCK_ENABLE); @@ -126,14 +126,14 @@ inline void TraceStreamerConfig::InitClkEventNameMap() eventNameMap_.emplace(TRACE_EVENT_CLK_DISABLE, TRACE_ACTION_CLK_DISABLE); eventNameMap_.emplace(TRACE_EVENT_CLOCK_SYNC, TRACE_ACTION_CLOCK_SYNC); } -inline void TraceStreamerConfig::InitCpuEventNameMap() +void TraceStreamerConfig::InitCpuEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_CPU_IDLE, TRACE_ACTION_CPU_IDLE); eventNameMap_.emplace(TRACE_EVENT_CPU_FREQUENCY, TRACE_ACTION_CPU_FREQUENCY); eventNameMap_.emplace(TRACE_EVENT_CPU_FREQUENCY_LIMITS, TRACE_ACTION_CPU_FREQUENCY_LIMITS); eventNameMap_.emplace(TRACE_CPU_USAGE, TRACE_ACTION_CPU_USAGE); } -inline void TraceStreamerConfig::InitInterruptEventNameMap() +void TraceStreamerConfig::InitInterruptEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_IPI_ENTRY, TRACE_ACTION_IPI_ENTRY); eventNameMap_.emplace(TRACE_EVENT_IPI_EXIT, TRACE_ACTION_IPI_EXIT); @@ -148,7 +148,7 @@ inline void TraceStreamerConfig::InitInterruptEventNameMap() eventNameMap_.emplace(TRACE_EVENT_DMA_FENCE_SIGNALED, TRACE_ACTION_DMA_FENCE_SIGNALED); eventNameMap_.emplace(TRACE_EVENT_DMA_FENCE, TRACE_ACTION_DMA_FENCE); } -inline void TraceStreamerConfig::InitMemoryEventNameMap() +void TraceStreamerConfig::InitMemoryEventNameMap() { eventNameMap_.emplace(TRACE_MEMORY, TRACE_ACTION_MEMORY); eventNameMap_.emplace(TRACE_SYS_MEMORY, TRACE_ACTION_SYS_MEMORY); @@ -159,7 +159,7 @@ inline void TraceStreamerConfig::InitMemoryEventNameMap() eventNameMap_.emplace(TRACE_JS_MEMORY, TRACE_ACTION_JS_MEMORY); eventNameMap_.emplace(TRACE_SYS_VIRTUAL_MEMORY, TRACE_ACTION_SYS_VIRTUAL_MEMORY); } -inline void TraceStreamerConfig::InitBlockEventNameMap() +void TraceStreamerConfig::InitBlockEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_BLOCK_BIO_BACKMERGE, TRACE_ACTION_BLOCK_BIO_BACKMERGE); eventNameMap_.emplace(TRACE_EVENT_BLOCK_BIO_BOUNCE, TRACE_ACTION_BLOCK_BIO_BOUNCE); @@ -175,14 +175,14 @@ inline void TraceStreamerConfig::InitBlockEventNameMap() eventNameMap_.emplace(TRACE_EVENT_BLOCK_RQ_REMAP, TRACE_ACTION_BLOCK_RQ_REMAP); eventNameMap_.emplace(TRACE_EVENT_BLOCK_RQ_ISSUE, TRACE_ACTION_BLOCK_RQ_ISSUE); } -inline void TraceStreamerConfig::InitRegulatorEventNameMap() +void TraceStreamerConfig::InitRegulatorEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_REGULATOR_SET_VOLTAGE, TRACE_ACTION_REGULATOR_SET_VOLTAGE); eventNameMap_.emplace(TRACE_EVENT_REGULATOR_SET_VOLTAGE_COMPLETE, TRACE_ACTION_REGULATOR_SET_VOLTAGE_COMPLETE); eventNameMap_.emplace(TRACE_EVENT_REGULATOR_DISABLE, TRACE_ACTION_REGULATOR_DISABLE); eventNameMap_.emplace(TRACE_EVENT_REGULATOR_DISABLE_COMPLETE, TRACE_ACTION_REGULATOR_DISABLE_COMPLETE); } -inline void TraceStreamerConfig::InitOtherEventNameMap() +void TraceStreamerConfig::InitOtherEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_FFRT, TRACE_ACTION_FFRT); eventNameMap_.emplace(TRACE_EVENT_PRINT, TRACE_ACTION_PRINT); @@ -213,14 +213,14 @@ inline void TraceStreamerConfig::InitOtherEventNameMap() eventNameMap_.emplace(TRACE_ON_DO_COMPOSITION, TRACE_ACTION_ON_DO_COMPOSITION); eventNameMap_.emplace(TRACE_FRAMEQUEUE, TRACE_ACTION_FRAMEQUEUE); } -inline void TraceStreamerConfig::InitEbpfEventNameMap() +void TraceStreamerConfig::InitEbpfEventNameMap() { eventNameMap_.emplace(TRACE_EVENT_EBPF, TRACE_ACTION_EBPF); eventNameMap_.emplace(TRACE_EVENT_EBPF_FILE_SYSTEM, TRACE_ACTION_EBPF_FILE_SYSTEM); eventNameMap_.emplace(TRACE_EVENT_EBPF_PAGED_MEMORY, TRACE_ACTION_EBPF_PAGED_MEMORY); eventNameMap_.emplace(TRACE_EVENT_EBPF_BIO_LATENCY, TRACE_ACTION_EBPF_BIO_LATENCY); } -inline void TraceStreamerConfig::InitHookEventNameMap() +void TraceStreamerConfig::InitHookEventNameMap() { eventNameMap_.emplace(TRACE_NATIVE_HOOK_MALLOC, TRACE_ACTION_NATIVE_HOOK_MALLOC); eventNameMap_.emplace(TRACE_NATIVE_HOOK_FREE, TRACE_ACTION_NATIVE_HOOK_FREE); @@ -285,7 +285,7 @@ void TraceStreamerConfig::InitSysMemMap() {SysMeminfoType::PMEM_INACTIVE_PURG, SYS_MEMINFO_INACTIVE_PURG_DESC}, {SysMeminfoType::PMEM_PINED_PURG, SYS_MEMINFO_PINED_PURG_DESC}}; } -inline void TraceStreamerConfig::InitNrZoneEventSysVmemMap() +void TraceStreamerConfig::InitNrZoneEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_ZONE_ACTIVE_ANON, SYS_VMEMINFO_NR_ZONE_ACTIVE_ANON_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_ZONE_ACTIVE_FILE, SYS_VMEMINFO_NR_ZONE_ACTIVE_FILE_DESC); @@ -297,7 +297,7 @@ inline void TraceStreamerConfig::InitNrZoneEventSysVmemMap() sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_ZONE_WRITE_PENDING, SYS_VMEMINFO_NR_ZONE_WRITE_PENDING_DESC); } -inline void TraceStreamerConfig::InitNrDirtierEventSysVmemMap() +void TraceStreamerConfig::InitNrDirtierEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_DIRTY, SYS_VMEMINFO_NR_DIRTY_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_DIRTY_THRESHOLD, SYS_VMEMINFO_NR_DIRTY_THRESHOLD_DESC); @@ -305,7 +305,7 @@ inline void TraceStreamerConfig::InitNrDirtierEventSysVmemMap() SYS_VMEMINFO_NR_DIRTY_BACKGROUND_THRESHOLD_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_DIRTIED, SYS_VMEMINFO_NR_DIRTIED_DESC); } -inline void TraceStreamerConfig::InitNrOtherEventSysVmemMap() +void TraceStreamerConfig::InitNrOtherEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_FREE_PAGES, SYS_VMEMINFO_NR_FREE_PAGES_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_ALLOC_BATCH, SYS_VMEMINFO_NR_ALLOC_BATCH_DESC); @@ -356,7 +356,7 @@ inline void TraceStreamerConfig::InitNrOtherEventSysVmemMap() sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_NR_UNRECLAIMABLE_PAGES, SYS_VMEMINFO_NR_UNRECLAIMABLE_PAGES_DESC); } -inline void TraceStreamerConfig::InitPgscanEventSysVmemMap() +void TraceStreamerConfig::InitPgscanEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSCAN_KSWAPD_DMA, SYS_VMEMINFO_PGSCAN_KSWAPD_DMA_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSCAN_KSWAPD_NORMAL, @@ -373,7 +373,7 @@ inline void TraceStreamerConfig::InitPgscanEventSysVmemMap() sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSCAN_DIRECT, SYS_VMEMINFO_PGSCAN_DIRECT_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSCAN_KSWAPD, SYS_VMEMINFO_PGSCAN_KSWAPD_DESC); } -inline void TraceStreamerConfig::InitPgstealEventSysVmemMap() +void TraceStreamerConfig::InitPgstealEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSTEAL_KSWAPD_DMA, SYS_VMEMINFO_PGSTEAL_KSWAPD_DMA_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSTEAL_KSWAPD_NORMAL, @@ -388,7 +388,7 @@ inline void TraceStreamerConfig::InitPgstealEventSysVmemMap() sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSTEAL_DIRECT, SYS_VMEMINFO_PGSTEAL_DIRECT_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSTEAL_KSWAPD, SYS_VMEMINFO_PGSTEAL_KSWAPD_DESC); } -inline void TraceStreamerConfig::InitCompactEventSysVmemMap() +void TraceStreamerConfig::InitCompactEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_COMPACT_MIGRATE_SCANNED, SYS_VMEMINFO_COMPACT_MIGRATE_SCANNED_DESC); @@ -404,7 +404,7 @@ inline void TraceStreamerConfig::InitCompactEventSysVmemMap() sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_COMPACT_DAEMON_MIGRATE_SCANNED, SYS_VMEMINFO_COMPACT_DAEMON_MIGRATE_SCANNED_DESC); } -inline void TraceStreamerConfig::InitUnevictableEventSysVmemMap() +void TraceStreamerConfig::InitUnevictableEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_UNEVICTABLE_PGS_CULLED, SYS_VMEMINFO_UNEVICTABLE_PGS_CULLED_DESC); @@ -421,14 +421,14 @@ inline void TraceStreamerConfig::InitUnevictableEventSysVmemMap() sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_UNEVICTABLE_PGS_STRANDED, SYS_VMEMINFO_UNEVICTABLE_PGS_STRANDED_DESC); } -inline void TraceStreamerConfig::InitPgreFillEventSysVmemMap() +void TraceStreamerConfig::InitPgreFillEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGREFILL_DMA, SYS_VMEMINFO_PGREFILL_DMA_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGREFILL_NORMAL, SYS_VMEMINFO_PGREFILL_NORMAL_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGREFILL_MOVABLE, SYS_VMEMINFO_PGREFILL_MOVABLE_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGREFILL, SYS_VMEMINFO_PGREFILL_DESC); } -inline void TraceStreamerConfig::InitWorkingSetEventSysVmemMap() +void TraceStreamerConfig::InitWorkingSetEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_WORKINGSET_REFAULT, SYS_VMEMINFO_WORKINGSET_REFAULT_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_WORKINGSET_ACTIVATE, SYS_VMEMINFO_WORKINGSET_ACTIVATE_DESC); @@ -436,7 +436,7 @@ inline void TraceStreamerConfig::InitWorkingSetEventSysVmemMap() SYS_VMEMINFO_WORKINGSET_NODERECLAIM_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_WORKINGSET_RESTORE, SYS_VMEMINFO_WORKINGSET_RESTORE_DESC); } -inline void TraceStreamerConfig::InitPgEventSysVmemMap() +void TraceStreamerConfig::InitPgEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGPGIN, SYS_VMEMINFO_PGPGIN_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGPGOUT, SYS_VMEMINFO_PGPGOUT_DESC); @@ -459,7 +459,7 @@ inline void TraceStreamerConfig::InitPgEventSysVmemMap() sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSKIP_MOVABLE, SYS_VMEMINFO_PGSKIP_MOVABLE_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PGSKIP_NORMAL, SYS_VMEMINFO_PGSKIP_NORMAL_DESC); } -inline void TraceStreamerConfig::InitOtherEventSysVmemMap() +void TraceStreamerConfig::InitOtherEventSysVmemMap() { sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_UNSPECIFIED, SYS_VMEMINFO_UNSPECIFIED_DESC); sysVirtualMemNameMap_.emplace(SysVMeminfoType::VMEMINFO_PSWPIN, SYS_VMEMINFO_PSWPIN_DESC); @@ -496,136 +496,6 @@ void TraceStreamerConfig::InitSysVmemMap() InitOtherEventSysVmemMap(); } #endif -inline void TraceStreamerConfig::InitBinderEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION_RECEIVED, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION_ALLOC_BUF, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION_LOCK, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION_LOCKED, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BINDER_TRANSACTION_UNLOCK, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitSchedEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SCHED_SWITCH, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SCHED_BLOCKED_REASON, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SCHED_WAKEUP, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SCHED_WAKING, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SCHED_WAKEUP_NEW, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitClkEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CLOCK_SET_RATE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CLOCK_ENABLE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CLOCK_DISABLE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CLK_SET_RATE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CLK_ENABLE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CLK_DISABLE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CLOCK_SYNC, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitCpuEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CPU_IDLE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CPU_FREQUENCY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_CPU_FREQUENCY_LIMITS, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_CPU_USAGE, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitInterruptEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_IPI_ENTRY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_IPI_EXIT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_IRQ_HANDLER_ENTRY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_IRQ_HANDLER_EXIT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SOFTIRQ_RAISE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SOFTIRQ_ENTRY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SOFTIRQ_EXIT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE_INIT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE_DESTROY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE_ENABLE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE_SIGNALED, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_DMA_FENCE, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitMemoryEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_MEMORY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_SYS_MEMORY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_ASHMEM, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_DMAMEM, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_GPU_PROCESS_MEM, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_GPU_WINDOW_MEM, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_JS_MEMORY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_SYS_VIRTUAL_MEMORY, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitBlockEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_BIO_BACKMERGE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_BIO_BOUNCE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_BIO_COMPLETE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_BIO_FRONTMERGE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_BIO_QUEUE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_BIO_REMAP, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_DIRTY_BUFFER, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_GETRQ, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_PLUG, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_RQ_COMPLETE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_RQ_INSERT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_RQ_REMAP, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_BLOCK_RQ_ISSUE, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitRegulatorEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_REGULATOR_SET_VOLTAGE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_REGULATOR_SET_VOLTAGE_COMPLETE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_REGULATOR_DISABLE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_REGULATOR_DISABLE_COMPLETE, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitOtherEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_FFRT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_PRINT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_TRACING_MARK_WRITE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_TASK_RENAME, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_TASK_NEWTASK, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SUSPEND_RESUME, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_WORKQUEUE_EXECUTE_START, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_WORKQUEUE_EXECUTE_END, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SYS_ENTRY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SYS_EXIT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_OOM_SCORE_ADJ_UPDATE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_PROCESS_EXIT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_PROCESS_FREE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SIGNAL_GENERATE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_SIGNAL_DELIVER, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_OTHER, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_DISKIO, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_PROCESS, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_NETWORK, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_PERF, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_HILOG, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_HIDUMP_FPS, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_HISYSEVENT, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_SMAPS, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_WINDOW_MANAGER_SERVICE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_VSYNC, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_ON_DO_COMPOSITION, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_FRAMEQUEUE, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitEbpfEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_EBPF, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_EBPF_FILE_SYSTEM, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_EBPF_PAGED_MEMORY, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_EVENT_EBPF_BIO_LATENCY, statSeverityDescMap_); -} -inline void TraceStreamerConfig::InitHookEventSecurityMap() -{ - eventParserStatSeverityDescMap_.emplace(TRACE_NATIVE_HOOK_MALLOC, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_NATIVE_HOOK_FREE, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_NATIVE_HOOK_MMAP, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_NATIVE_HOOK_MUNMAP, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_NATIVE_HOOK_RECORD_STATISTICS, statSeverityDescMap_); - eventParserStatSeverityDescMap_.emplace(TRACE_NATIVE_HOOK_MEMTAG, statSeverityDescMap_); -} void TraceStreamerConfig::InitSecurityMap() { statSeverityDescMap_ = { @@ -633,17 +503,9 @@ void TraceStreamerConfig::InitSecurityMap() {STAT_EVENT_NOTMATCH, STAT_SEVERITY_LEVEL_INFO}, {STAT_EVENT_NOTSUPPORTED, STAT_SEVERITY_LEVEL_WARN}, {STAT_EVENT_DATA_INVALID, STAT_SEVERITY_LEVEL_ERROR}, }; - InitBinderEventSecurityMap(); - InitSchedEventSecurityMap(); - InitClkEventSecurityMap(); - InitCpuEventSecurityMap(); - InitInterruptEventSecurityMap(); - InitMemoryEventSecurityMap(); - InitBlockEventSecurityMap(); - InitRegulatorEventSecurityMap(); - InitOtherEventSecurityMap(); - InitEbpfEventSecurityMap(); - InitHookEventSecurityMap(); + for (int i = 0; i < TRACE_EVENT_MAX; i++) { + eventParserStatSeverityDescMap_.emplace(static_cast(i), statSeverityDescMap_); + } } } // namespace TraceCfg } // namespace SysTuning diff --git a/trace_streamer/src/cfg/trace_streamer_config.h b/trace_streamer/src/cfg/trace_streamer_config.h index 12db21c82..7b4c28bd2 100644 --- a/trace_streamer/src/cfg/trace_streamer_config.h +++ b/trace_streamer/src/cfg/trace_streamer_config.h @@ -228,19 +228,7 @@ private: void InitPgEventSysVmemMap(); void InitOtherEventSysVmemMap(); #endif - void InitSecurityMap(); - void InitBinderEventSecurityMap(); - void InitSchedEventSecurityMap(); - void InitClkEventSecurityMap(); - void InitCpuEventSecurityMap(); - void InitInterruptEventSecurityMap(); - void InitMemoryEventSecurityMap(); - void InitBlockEventSecurityMap(); - void InitRegulatorEventSecurityMap(); - void InitOtherEventSecurityMap(); - void InitEbpfEventSecurityMap(); - void InitHookEventSecurityMap(); // all supported events should be defined here, these str can be find in text-based trace const std::string TRACE_ACTION_BINDER_TRANSACTION = "binder_transaction"; const std::string TRACE_ACTION_BINDER_TRANSACTION_RECEIVED = "binder_transaction_received"; diff --git a/trace_streamer/src/filter/binder_filter.h b/trace_streamer/src/filter/binder_filter.h index d4eb5a04d..b480ea1da 100644 --- a/trace_streamer/src/filter/binder_filter.h +++ b/trace_streamer/src/filter/binder_filter.h @@ -81,8 +81,8 @@ private: const DataIndex nullStringId_ = traceDataCache_->GetDataIndex("null"); std::unordered_map lastEventTs_ = {}; std::unordered_map transReplyDest_ = {}; - std::unordered_map> transactionInfo_ = - {}; // 记录每一个transaction的src_tid和dest_tid + // 记录每一个transaction的src_tid和dest_tid + std::unordered_map> transactionInfo_ = {}; std::unordered_map transReplyFilter_ = {}; std::unordered_map asyncBinderEvents_ = {}; std::unordered_map binderFlagDescs_ = {}; diff --git a/trace_streamer/src/rpc/ffrt_converter.cpp b/trace_streamer/src/rpc/ffrt_converter.cpp index 55cb03c43..6da27f8a5 100644 --- a/trace_streamer/src/rpc/ffrt_converter.cpp +++ b/trace_streamer/src/rpc/ffrt_converter.cpp @@ -243,7 +243,6 @@ std::string FfrtConverter::ConvertWorkerLogToTask(const std::string &mark, } int FfrtConverter::FindIntNumberAfterStr(const size_t index, const string &str) { - auto beginPos = context_[index].find(str); if (beginPos != std::string::npos) { beginPos = beginPos + str.length(); -- Gitee From 3eedbfc69e675b76fa8f498dfb98f9b8ab514909 Mon Sep 17 00:00:00 2001 From: jichuan Date: Mon, 24 Jun 2024 14:39:32 +0800 Subject: [PATCH 3/6] fix: subtypeId change Signed-off-by: jichuan --- trace_streamer/src/filter/hook_filter/native_hook_filter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trace_streamer/src/filter/hook_filter/native_hook_filter.cpp b/trace_streamer/src/filter/hook_filter/native_hook_filter.cpp index 88d46e1e8..283a6f683 100644 --- a/trace_streamer/src/filter/hook_filter/native_hook_filter.cpp +++ b/trace_streamer/src/filter/hook_filter/native_hook_filter.cpp @@ -403,7 +403,7 @@ void NativeHookFilter::ParseMmapEvent(uint64_t timeStamp, const ProtoReader::Byt DataIndex subType = INVALID_UINT64; auto mMapAddr = mMapEventReader.addr(); auto mMapSize = mMapEventReader.size(); - if (mMapEventReader.has_type()) { + if (mMapEventReader.has_type() && !mMapEventReader.type().ToStdString().empty()) { subType = traceDataCache_->dataDict_.GetStringIndex(mMapEventReader.type().ToStdString()); // Establish a mapping of addr and size to the mmap tag index. addrToMmapTag_[mMapAddr] = subType; // update addr to MemMapSubType -- Gitee From ee8cb56c4cde8ecc69d6503b62f13d51869451dd Mon Sep 17 00:00:00 2001 From: jichuan Date: Mon, 24 Jun 2024 14:51:47 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=96=B0binder=E7=89=B9=E6=80=A7=E5=92=8C?= =?UTF-8?q?=E6=97=A7=E6=A8=A1=E5=BC=8F=E4=B8=8D=E5=AE=8C=E5=85=A8=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=EF=BC=8C=E5=9B=9E=E9=80=80=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jichuan --- trace_streamer/src/filter/binder_filter.cpp | 45 ++++++++------------- trace_streamer/src/filter/binder_filter.h | 5 +-- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/trace_streamer/src/filter/binder_filter.cpp b/trace_streamer/src/filter/binder_filter.cpp index 63f674453..a7cf6a7c1 100644 --- a/trace_streamer/src/filter/binder_filter.cpp +++ b/trace_streamer/src/filter/binder_filter.cpp @@ -88,21 +88,14 @@ void BinderFilter::SendTraction(int64_t ts, } // the flowing code should be under the ubove conditions, but this will bring a big impact to the UI-SHOW (void)streamFilters_->sliceFilter_->EndBinder(ts, tid, INVALID_UINT64, INVALID_UINT64, argsSend); - transReplyDest_[transactionId] = destTid; + transReplyWaitingReply_.insert(transactionId); return; } else { bool needReply = !isReply && !(flags & noReturnMsgFlag_); if (needReply) { // transaction needs reply TAG-1 (void)streamFilters_->sliceFilter_->BeginBinder(ts, tid, binderCatalogId_, transSliceId_, argsSend); - if (destTid != 0) { - // 如果transaction携带了destTid,保存在transReplyFilter_中 - transReplyFilter_[destTid] = tid; - transactionInfo_[transactionId] = std::make_pair(tid, destTid); - } else { - // 没有携带destTid,以receiver的线程作为destTid - transactionInfo_[transactionId] = std::make_pair(tid, INVALID_UINT32); - } + transNeedReply_[transactionId] = tid; } else { // transaction do not need reply // tid calling id @@ -114,29 +107,21 @@ void BinderFilter::SendTraction(int64_t ts, } void BinderFilter::ReceiveTraction(int64_t ts, uint32_t pid, uint64_t transactionId) { - if (transReplyDest_.count(transactionId)) { - (void)streamFilters_->sliceFilter_->EndBinder(ts, transReplyDest_[transactionId]); - transReplyDest_.erase(transactionId); + InternalTid internalTid = streamFilters_->processFilter_->UpdateOrCreateThread(ts, pid); + const auto threadName = traceDataCache_->GetConstThreadData(internalTid).nameIndex_; + if (transReplyWaitingReply_.count(transactionId)) { + (void)streamFilters_->sliceFilter_->EndBinder(ts, pid); + transReplyWaitingReply_.erase(transactionId); return; } - if (transactionInfo_.count(transactionId)) { - uint32_t replyTid; - if (transactionInfo_[transactionId].second == INVALID_UINT32) { - // binder transaction 没有携带destId, 取当前binder transaction received的线程id为transaction destTid - replyTid = pid; - transReplyFilter_[pid] = transactionInfo_[transactionId].first; - } else { - replyTid = transactionInfo_[transactionId].second; - } + if (transNeedReply_.count(transactionId)) { // First, begin the reply, the reply will be end in "SendTraction" func, and the isReply will be true, TAG-2 - auto replySliceid = streamFilters_->sliceFilter_->BeginBinder(ts, replyTid, binderCatalogId_, replyId_); - + auto replySliceid = streamFilters_->sliceFilter_->BeginBinder(ts, pid, binderCatalogId_, replyId_); + transReplyFilter_[pid] = transNeedReply_[transactionId]; // Add dest info to the reply - InternalTid internalTid = streamFilters_->processFilter_->UpdateOrCreateThread(ts, replyTid); - const auto threadName = traceDataCache_->GetConstThreadData(internalTid).nameIndex_; ArgsSet args; - args.AppendArg(destThreadId_, BASE_DATA_TYPE_INT, replyTid); + args.AppendArg(destThreadId_, BASE_DATA_TYPE_INT, pid); args.AppendArg(destThreadNameId_, BASE_DATA_TYPE_STRING, threadName); if (IsValidUint32(static_cast(replySliceid))) { args.AppendArg(destSliceId_, BASE_DATA_TYPE_INT, replySliceid); @@ -147,7 +132,7 @@ void BinderFilter::ReceiveTraction(int64_t ts, uint32_t pid, uint64_t transactio // Add dest args uint64_t transSliceId = INVALID_UINT32; uint32_t argSetId = INVALID_UINT32; - std::tie(transSliceId, argSetId) = streamFilters_->sliceFilter_->AddArgs(transactionInfo_[transactionId].first, + std::tie(transSliceId, argSetId) = streamFilters_->sliceFilter_->AddArgs(transNeedReply_[transactionId], binderCatalogId_, transSliceId_, args); // remeber dest slice-id to the argset from "SendTraction" TAG-1 @@ -159,9 +144,9 @@ void BinderFilter::ReceiveTraction(int64_t ts, uint32_t pid, uint64_t transactio return; } std::tie(transSliceId, argSetId) = - streamFilters_->sliceFilter_->AddArgs(replyTid, binderCatalogId_, replyId_, replyDestInserter); + streamFilters_->sliceFilter_->AddArgs(pid, binderCatalogId_, replyId_, replyDestInserter); traceDataCache_->GetInternalSlicesData()->SetArgSetId(transSliceId, argSetId); - transactionInfo_.erase(transactionId); + transNeedReply_.erase(transactionId); return; } // the code below can be hard to understand, may be a EndBinder will be better @@ -218,6 +203,8 @@ bool BinderFilter::IsAsync(int32_t flags) const void BinderFilter::Clear() { lastEventTs_.clear(); + transReplyWaitingReply_.clear(); + transNeedReply_.clear(); asyncBinderEvents_.clear(); } } // namespace TraceStreamer diff --git a/trace_streamer/src/filter/binder_filter.h b/trace_streamer/src/filter/binder_filter.h index b480ea1da..30dcca0a9 100644 --- a/trace_streamer/src/filter/binder_filter.h +++ b/trace_streamer/src/filter/binder_filter.h @@ -80,9 +80,8 @@ private: const DataIndex dataOffsetSizeId_ = traceDataCache_->GetDataIndex("offsets size"); const DataIndex nullStringId_ = traceDataCache_->GetDataIndex("null"); std::unordered_map lastEventTs_ = {}; - std::unordered_map transReplyDest_ = {}; - // 记录每一个transaction的src_tid和dest_tid - std::unordered_map> transactionInfo_ = {}; + std::unordered_set transReplyWaitingReply_ = {}; + std::unordered_map transNeedReply_ = {}; std::unordered_map transReplyFilter_ = {}; std::unordered_map asyncBinderEvents_ = {}; std::unordered_map binderFlagDescs_ = {}; -- Gitee From b559ed779f59c0525694ae0e32ad94ce2157fb0a Mon Sep 17 00:00:00 2001 From: jichuan Date: Mon, 24 Jun 2024 15:13:46 +0800 Subject: [PATCH 5/6] fix: support for checking kernel type Signed-off-by: jichuan --- trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp b/trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp index 127228fa5..b7b515d38 100644 --- a/trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp +++ b/trace_streamer/src/parser/hiperf_parser/perf_data_parser.cpp @@ -737,6 +737,8 @@ void PerfDataParser::SetHM() { std::string os = recordDataReader_->GetFeatureString(FEATURE::OSRELEASE); auto isHM = os.find(HMKERNEL) != std::string::npos; + isHM = isHM || os.find("hmkernel") != std::string::npos; + isHM = isHM || os.find("HongMeng") != std::string::npos; report_->virtualRuntime_.SetHM(isHM); if (isHM) { pid_t devhost = -1; -- Gitee From 6c2f1994116a0f31f52425460de6a8f03d19ffe7 Mon Sep 17 00:00:00 2001 From: jichuan Date: Mon, 24 Jun 2024 15:42:01 +0800 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8D=E5=86=85?= =?UTF-8?q?=E6=A0=B8=E6=96=B0tracing=5Fmark=5Fwriting=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jichuan --- .../rawtrace_parser/ftrace_event_processor.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.cpp b/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.cpp index 0dded97b8..58e0c9c5f 100644 --- a/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.cpp +++ b/trace_streamer/src/parser/rawtrace_parser/ftrace_event_processor.cpp @@ -13,6 +13,9 @@ * limitations under the License. */ #include "ftrace_event_processor.h" +#include "rawtrace_parser/ftrace_field_processor.h" +#include +#include namespace SysTuning { namespace TraceStreamer { @@ -431,7 +434,17 @@ bool FtraceEventProcessor::TracingMarkWriteOrPrintFormat(FtraceEvent &ftraceEven if (format.eventId < HM_EVENT_ID_OFFSET) { printMsg->set_ip(FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size)); } - printMsg->set_buf(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + // size大于60时为适配内核的event,小于则为原始的event + if (format.eventSize > 60) { + auto pid = FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size); + auto name = FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size); + auto start = FtraceFieldProcessor::HandleIntField(format.fields, index++, data, size); + auto msg = std::string(start == 0 ? "E" : "B") + std::string("|") + std::to_string(pid) + std::string("|") + + std::string(start == 0 ? "" : name); + printMsg->set_buf(msg); + } else { + printMsg->set_buf(FtraceFieldProcessor::HandleStrField(format.fields, index++, data, size)); + } return true; } -- Gitee