diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..e454a52 --- /dev/null +++ b/LICENSE @@ -0,0 +1,178 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 6013ddf..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# developtools_bytrace_standard - -#### Description -A tool to trace processes and monitor performance | 提供性能追踪的接口和查看性能轨迹的命令行工具 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md deleted file mode 100644 index 1a117d4..0000000 --- a/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# developtools_bytrace_standard - -#### 介绍 -A tool to trace processes and monitor performance | 提供性能追踪的接口和查看性能轨迹的命令行工具 - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README_zh.md b/README_zh.md new file mode 100755 index 0000000..67a8773 --- /dev/null +++ b/README_zh.md @@ -0,0 +1,148 @@ +# bytrace组件 + +- [简介](#section152771918494) +- [架构图](#section6808195518497) +- [目录](#section1610792125019) +- [说明](#section18684185975017) +- [相关仓](#section1849151125618) + +## 简介 + +bytrace是开发人员用于追踪进程轨迹、查看性能的一种工具,主要对内核ftrace进行了封装和扩展,来支持用户态的打点。该工具主要分为两部分,API和命令行: + +1. bytrace向应用开发人员暴露了打点的API,开发应用过程中可以在关键代码处调用对应API进行打点; +2. 命令行部分通过使能对应的label,来获取打点信息。通过该工具可以打开想要查看的用户态和内核label(通过命令行“bytrace -l”,查看支持的所有label),然后通过命令行进行抓取trace信息到指定文件中,下文有具体使用指导。 + +## 架构图 + +![](figures/20210422-202314(WeLinkPC).png) + +## 目录 + +``` +/developtools/bytrace_standard +├── bin # bytrace组件代码目录 +│ └── include # 头文件目录 +│ └── src # 源文件目录 +│ └── test # 测试用例目录 +├── interfaces # 对外接口存放目录 +│ └── innerkits # 对内部子系统暴露的头文件存放目录 +│ └── kits # 对外部暴露的头文件存放目录 +├── script # 脚本目录 +``` + +## 说明 + +使用说明 + +bytrace当前支持如下命令: + +**表 1** 命令行列表 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Option

+

Description

+

-h,--help

+

查看option帮助

+

-b n,--buffer_size n

+

指定n(KB)内存大小用于存取trace日志,默认2048KB

+

-t n,--time n

+

用来指定trace运行的时间(单位:s),取决于需要分析过程的时间

+

--trace_clock clock

+

trace输出的时钟类型,一般设备支持boot、global、mono、uptime、perf等,默认为boot

+

--trace_begin

+

启动抓trace

+

--trace_dump

+

将数据输出到指定位置(默认控制台)

+

--trace_finish

+

停止抓trace,并将数据输出到指定位置(默认控制台)

+

-l,--list_categories

+

输出手机能支持的trace模块

+

--overwrite

+

当缓冲区满的时候,将丢弃最新的信息。(默认丢弃最老的日志)

+

-o filename,--output filename

+

指定输出的目标文件名称

+

-z

+

抓取trace后进行压缩

+
+ +以下是常用bytrace命令示例,供开发者参考: + +- 查询支持的label。 + + ``` + bytrace -l + ``` + + 或者 + + ``` + bytrace --list_categories + ``` + + +- 设置4M缓存,抓取10秒,抓取label为ability的trace信息。 + + ``` + bytrace -b 4096 -t 10 --overwrite ability > /data/mytrace.ftrace + ``` + + +- 设置trace的输出时钟为mono。 + + ``` + bytrace --trace_clock mono -b 4096 -t 10 --overwrite ability > /data/mytrace.ftrace + ``` + + +- 抓取trace后进行压缩。 + + ``` + bytrace -z -b 4096 -t 10 --overwrite ability > /data/mytrace.ftrace + ``` + + +## 相关仓 + +研发工具链子系统 + +**developtools\_bytrace\_standard** + diff --git a/bin/BUILD.gn b/bin/BUILD.gn new file mode 100755 index 0000000..3d690d0 --- /dev/null +++ b/bin/BUILD.gn @@ -0,0 +1,73 @@ +# Copyright (C) 2021 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") +import("//developtools/bytrace_standard/bytrace.gni") + +config("bytrace_inner_config") { + visibility = [ ":*" ] + include_dirs = [ "${innerkits_path}/native/include" ] +} + +ohos_static_library("bytrace_inner") { + sources = [ "./src/bytrace_impl.cpp" ] + public_configs = [ ":bytrace_inner_config" ] + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "startup_l2:syspara", + + #"distributedschedule:samgr_L2" + ] +} + +config("bytrace_capture_inner_config") { + visibility = [ ":*" ] + include_dirs = [ + "./include", + "${innerkits_path}/native/include", + ] +} + +ohos_static_library("bytrace_capture_inner") { + sources = [ "./src/bytrace_capture.cpp" ] + public_configs = [ ":bytrace_capture_inner_config" ] + external_deps = [ + "ipc:ipc_core", + "startup_l2:syspara", + ] +} + +ohos_executable("bytrace") { + install_enable = true + sources = [ "./src/bytrace.cpp" ] + + deps = [ + ":bytrace_capture_inner", + "${innerkits_path}/native:bytrace_core", + "//third_party/zlib:libz", + "//utils/native/base:utils", + ] + include_dirs = [ + "${bytrace_path}/bin/include", + "${innerkits_path}/include", + "//utils/native/base/include", + "//third_party/zlib", + ] + subsystem_name = "developtools" + part_name = "bytrace_standard" +} + +group("bytrace_target") { + deps = [ ":bytrace" ] +} diff --git a/bin/include/bytrace_capture.h b/bin/include/bytrace_capture.h new file mode 100755 index 0000000..f130dc1 --- /dev/null +++ b/bin/include/bytrace_capture.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * 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 DEVELOPTOOLS_BYTRACE_ADAPTER_INCLUDE_BYTRACE_CAPTURE_H +#define DEVELOPTOOLS_BYTRACE_ADAPTER_INCLUDE_BYTRACE_CAPTURE_H + +#include +#include + +const int MAX_SYS_FILES = 11; +enum TraceType { USER, KERNEL }; +struct TagCategory { + std::string name; + std::string description; + uint64_t tag; + TraceType type; + struct { + std::string path; + } sysfiles[MAX_SYS_FILES]; +}; + +std::string GetPropertyInner(const std::string& property, const std::string& value); +bool SetPropertyInner(const std::string& property, const std::string& value); +void RefreshBinderServices(); +bool RefreshHalServices(); +#endif // DEVELOPTOOLS_BYTRACE_ADAPTER_INCLUDE_BYTRACE_CAPTURE_H diff --git a/bin/src/bytrace.cpp b/bin/src/bytrace.cpp new file mode 100755 index 0000000..83add79 --- /dev/null +++ b/bin/src/bytrace.cpp @@ -0,0 +1,867 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * 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 "bytrace.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bytrace_capture.h" +#include "securec.h" + +using namespace std; +namespace { +struct option g_longOptions[] = { + { "buffer_size", required_argument, nullptr, 0 }, + { "trace_clock", required_argument, nullptr, 0 }, + { "help", no_argument, nullptr, 0 }, + { "output", required_argument, nullptr, 0 }, + { "time", required_argument, nullptr, 0 }, + { "trace_begin", no_argument, nullptr, 0 }, + { "trace_finish", no_argument, nullptr, 0 }, + { "trace_dump", no_argument, nullptr, 0 }, + { "list_categories", no_argument, nullptr, 0 }, + { "overwrite", no_argument, nullptr, 0 }, +}; +const int CHUNK_SIZE = 65536; +const int BLOCK_SIZE = 4096; + +const string TRACE_TAG_PROPERTY = "debug.bytrace.tags.enableflags"; + +// various operating paths of ftrace +const string TRACING_ON_PATH = "tracing_on"; +const string TRACE_PATH = "trace"; +const string TRACE_MARKER_PATH = "trace_marker"; +const string BUFFER_SIZE_PATH = "buffer_size_kb"; +const string CURRENT_TRACER_PATH = "current_tracer"; +const string TRACE_CLOCK_PATH = "trace_clock"; +const string OVER_WRITE_PATH = "options/overwrite"; +const string RECORD_TGID_PATH = "options/record-tgid"; + +// support customization of some parameters + +const int MIN_BUFFER_SIZE = 256; +const int MAX_BUFFER_SIZE = 307200; // 300 MB +constexpr unsigned int MAX_OUTPUT_LEN = 255; +const int PAGE_SIZE_KB = 4; // 4 KB +int g_traceDuration = 5; +int g_bufferSizeKB = 2048; +string g_clock = "boot"; +bool g_overwrite = true; +string g_outputFile; +bool g_compress = false; + +string g_traceRootPath; + +bool g_traceStart = true; +bool g_traceStop = true; +bool g_traceDump = true; + +map g_tagMap; +vector g_userEnabledTags; +vector g_kernelEnabledPaths; +} + +static bool IsTraceMounted() +{ + const string debugfsPath = "/sys/kernel/debug/tracing/"; + const string tracefsPath = "/sys/kernel/tracing/"; + + if (access((debugfsPath + TRACE_MARKER_PATH).c_str(), F_OK) != -1) { + g_traceRootPath = debugfsPath; + return true; + } + if (access((tracefsPath + TRACE_MARKER_PATH).c_str(), F_OK) != -1) { + g_traceRootPath = tracefsPath; + return true; + } + + fprintf(stderr, "Error: Did not find trace folder\n"); + return false; +} + +static bool IsFileExit(const string& filename) +{ + return access((g_traceRootPath + filename).c_str(), F_OK) != -1; +} + +static bool IsWritableFile(const string& filename) +{ + return access((g_traceRootPath + filename).c_str(), W_OK) != -1; +} + +static bool WriteStrToFile(const string& filename, const std::string& str) +{ + ofstream out; + out.open(g_traceRootPath + filename, ios::out); + if (out.fail()) { + fprintf(stderr, "Error: Did not open %s.\n", filename.c_str()); + return false; + } + out << str; + if (out.bad()) { + fprintf(stderr, "Error: Did not write %s.\n", filename.c_str()); + out.close(); + return false; + } + out.close(); + return true; +} + +static bool SetFtraceEnabled(const string& path, bool enabled) +{ + return WriteStrToFile(path, enabled ? "1" : "0"); +} + +static bool IsTagSupported(const string& name) +{ + auto it = g_tagMap.find(name); + if (it == g_tagMap.end()) { + return false; + } + + TagCategory tagCategory = it->second; + if (tagCategory.type != KERNEL) { + g_userEnabledTags.push_back(tagCategory.tag); + return true; + } + + for (int i = 0; i < MAX_SYS_FILES; i++) { + const string path = tagCategory.sysfiles[i].path; + if (path.size() == 0) { + continue; + } + if (IsWritableFile(path)) { + g_kernelEnabledPaths.push_back(path); + } else if (IsFileExit(path)) { + fprintf(stderr, "Warning: category \"%s\" requires root " + "privileges.\n", name.c_str()); + return false; + } else { + return false; + } + } + return true; +} + +static string ReadFile(const string& filename) +{ + ifstream fin((g_traceRootPath + filename).c_str()); + if (!fin.is_open()) { + fprintf(stderr, "open file: %s failed!", (g_traceRootPath + filename).c_str()); + return ""; + } + + string str((istreambuf_iterator(fin)), istreambuf_iterator()); + fin.close(); + return str; +} + +static bool SetBufferSize(int bufferSize) +{ + if (!WriteStrToFile(CURRENT_TRACER_PATH, "nop")) { + fprintf(stderr, "Error: write \"nop\" to %s.\n", CURRENT_TRACER_PATH.c_str()); + } + return WriteStrToFile(BUFFER_SIZE_PATH, to_string(bufferSize)); +} + +static bool SetClock(const string& timeclock) +{ + string allClocks = ReadFile(TRACE_CLOCK_PATH); + size_t begin = allClocks.find("["); + size_t end = allClocks.find("]"); + string newClock; + if (begin != string::npos && end != string::npos && + timeclock.compare(0, timeclock.size(), allClocks, begin + 1, end - begin - 1) >= 0) { + return true; + } else if (allClocks.find(timeclock) != string::npos) { + newClock = timeclock; + } else if (allClocks.find("boot") != string::npos) { + // boot: This is the boot clock (CLOCK_BOOTTIME) and is based on the fast monotonic clock, + // but also accounts for time in suspend. + newClock = "boot"; + } else if (allClocks.find("mono") != string::npos) { + // mono: uses the fast monotonic clock (CLOCK_MONOTONIC) + // which is monotonic and is subject to NTP rate adjustments. + newClock = "mono"; + } else if (allClocks.find("global") != string::npos) { + // global: is in sync with all CPUs but may be a bit slower than the local clock. + newClock = "global"; + } else { + printf("You can set trace clock in %s\n", allClocks.c_str()); + return false; + } + if (newClock.size() != 0) { + return WriteStrToFile(TRACE_CLOCK_PATH, newClock); + } + return true; +} + +static bool SetOverWriteEnable(bool enabled) +{ + return SetFtraceEnabled(OVER_WRITE_PATH, enabled); +} + +static bool SetTgidEnable(bool enabled) +{ + return SetFtraceEnabled(RECORD_TGID_PATH, enabled); +} + +static bool DisableAllFtraceEvents() +{ + bool isTrue = true; + for (auto it = g_tagMap.begin(); it != g_tagMap.end(); ++it) { + TagCategory tag = it->second; + if (tag.type != KERNEL) { + continue; + } + for (int i = 0; i < MAX_SYS_FILES; i++) { + const string path = tag.sysfiles[i].path; + if (path.size() > 0 && IsWritableFile(path)) { + isTrue = isTrue && SetFtraceEnabled(path, false); + } + } + } + return isTrue; +} + +static bool SetProperty(const string& property, const string& value) +{ + return SetPropertyInner(property, value); +} + +static bool SetTraceTagsEnabled(uint64_t tags) +{ + string value = to_string(tags); + return SetProperty(TRACE_TAG_PROPERTY, value); +} + +static bool RefreshServices() +{ + RefreshBinderServices(); + return RefreshHalServices(); +} + +static bool SetUserSpaceSettings() +{ + uint64_t enabledTags = 0; + for (auto tag: g_userEnabledTags) { + enabledTags |= tag; + } + return SetTraceTagsEnabled(enabledTags) && RefreshServices(); +} + +static bool ClearUserSpaceSettings() +{ + return SetTraceTagsEnabled(0) && RefreshServices(); +} + +static bool SetKernelSpaceSettings() +{ + bool isTrue = SetBufferSize(g_bufferSizeKB) && SetClock(g_clock) && + SetOverWriteEnable(g_overwrite) && DisableAllFtraceEvents(); + for (const auto& path : g_kernelEnabledPaths) { + SetFtraceEnabled(path, true); + } + return isTrue; +} + +static bool ClearKernelSpaceSettings() +{ + return DisableAllFtraceEvents() && SetOverWriteEnable(true) && SetBufferSize(1) && SetClock("boot"); +} + +static bool SetViewStyle() +{ + return SetTgidEnable(true); +} + +static void ShowListCategory() +{ + printf(" %18s description:\n", "tagName:"); + for (auto it = g_tagMap.begin(); it != g_tagMap.end(); ++it) { + string key = it->first; + TagCategory tag = it->second; + if (IsTagSupported(key)) { + printf(" %18s - %s\n", tag.name.c_str(), tag.description.c_str()); + } + } +} + +static void ShowHelp(const string& cmd) +{ + printf("usage: %s [options] [categories...]\n", cmd.c_str()); + printf("options include:\n" + " -b N Sets the size of the buffer (KB) for storing and reading traces. The default \n" + " buffer size is 2048 KB.\n" + " --buffer_size N Like \"-b N\".\n" + " -l Lists available bytrace categories.\n" + " --list_categories Like \"-l\".\n" + " -t N Sets the bytrace running duration in seconds (5s by default), which depends on" + " the time required for analysis.\n" + " --time N Like \"-t N\".\n" + " --trace_clock clock\n" + " Sets the type of the clock for adding a timestamp to a trace, which can be\n" + " boot (default), global, mono, uptime, or perf.\n" + " --trace_begin Starts capturing traces.\n" + " --trace_dump Dumps traces to a specified path (stdout by default).\n" + " --trace_finish Stops capturing traces and dumps traces to a specified path (stdout by default).\n" + " --overwrite Sets the action to take when the buffer is full. If this option is used,\n" + " the latest traces are discarded; if this option is not used (default setting),\n" + " the earliest traces are discarded.\n" + " -o filename Specifies the name of the target file (stdout by default).\n" + " --output filename\n" + " Like \"-o filename\".\n" + " -z Compresses a captured trace.\n" + ); +} + +template +inline bool StrToNum(const std::string& sString, T &tX) +{ + std::istringstream iStream(sString); + return (iStream >> tX) ? true : false; +} + +static void ParseLongOpt(const string& cmd, + int optionIndex, + bool& isTrue) +{ + if (!strcmp(g_longOptions[optionIndex].name, "buffer_size")) { + if (!StrToNum(optarg, g_bufferSizeKB)) { + printf("Error: the bufferSize is illegal input. eg: \"--buffer_size 1024.\"\n"); + isTrue &= false; + } else if (g_bufferSizeKB < MIN_BUFFER_SIZE || g_bufferSizeKB > MAX_BUFFER_SIZE) { + printf("Error: the buffer size should be within 256 KB to 300 MB. eg: \"--buffer_size 1024.\"\n"); + isTrue &= false; + } + g_bufferSizeKB = g_bufferSizeKB / PAGE_SIZE_KB * PAGE_SIZE_KB; + } else if (!strcmp(g_longOptions[optionIndex].name, "trace_clock")) { + regex re("[a-zA-Z]{4,6}"); + if (regex_match(optarg, re)) { + g_clock = optarg; + } else { + printf("Error: \"--trace_clock\" is illegal input. eg: \"--trace_clock boot.\"\n"); + isTrue &= false; + } + } else if (!strcmp(g_longOptions[optionIndex].name, "help")) { + ShowHelp(cmd); + isTrue &= false; + } else if (!strcmp(g_longOptions[optionIndex].name, "time")) { + if (!StrToNum(optarg, g_traceDuration)) { + printf("Error: the time is illegal input. eg: \"--time 5.\"\n"); + isTrue &= false; + } else if (g_traceDuration < 1) { + printf("Error: \"-t %s\" to be greater than zero. eg: \"--time 5\"\n", optarg); + isTrue &= false; + } + } else if (!strcmp(g_longOptions[optionIndex].name, "list_categories")) { + ShowListCategory(); + isTrue &= false; + } else if (!strcmp(g_longOptions[optionIndex].name, "output")) { + struct stat buf; + size_t len = strnlen(optarg, MAX_OUTPUT_LEN); + if (len == MAX_OUTPUT_LEN || len < 1 || + (stat(optarg, &buf) == 0 && (buf.st_mode & S_IFDIR) != 0)) { + printf("Error: output file is illegal.\n"); + isTrue &= false; + } else { + g_outputFile = optarg; + } + } else if (!strcmp(g_longOptions[optionIndex].name, "overwrite")) { + g_overwrite = false; + } else if (!strcmp(g_longOptions[optionIndex].name, "trace_begin")) { + g_traceStart = true; + g_traceStop = false; + g_traceDump = false; + } else if (!strcmp(g_longOptions[optionIndex].name, "trace_finish")) { + g_traceStart = false; + g_traceStop = true; + g_traceDump = true; + } else if (!strcmp(g_longOptions[optionIndex].name, "trace_dump")) { + g_traceStart = false; + g_traceStop = false; + g_traceDump = true; + } +} + +static bool ParseOpt(int opt, char** argv, int optIndex) +{ + bool isTrue = true; + switch (opt) { + case 'b': { + if (!StrToNum(optarg, g_bufferSizeKB)) { + printf("Error: the bufferSize is illegal input. eg: \"--buffer_size 1024.\"\n"); + isTrue &= false; + } else if (g_bufferSizeKB < MIN_BUFFER_SIZE || g_bufferSizeKB > MAX_BUFFER_SIZE) { + printf("Error: the buffer size should be within 256 KB to 300 MB. eg: \"--buffer_size 1024.\"\n"); + isTrue &= false; + } + g_bufferSizeKB = g_bufferSizeKB / PAGE_SIZE_KB * PAGE_SIZE_KB; + break; + } + case 'h': + ShowHelp(argv[0]); + isTrue &= false; + break; + case 'l': + ShowListCategory(); + isTrue &= false; + break; + case 't': { + if (!StrToNum(optarg, g_traceDuration)) { + printf("Error: the time is illegal input. eg: \"--time 5.\"\n"); + isTrue &= false; + } else if (g_traceDuration < 1) { + printf("Error: \"-t %s\" to be greater than zero. eg: \"--time 5\"\n", optarg); + isTrue &= false; + } + break; + } + case 'o': { + struct stat buf; + size_t len = strnlen(optarg, MAX_OUTPUT_LEN); + if (len == MAX_OUTPUT_LEN || len < 1 || + (stat(optarg, &buf) == 0 && (buf.st_mode & S_IFDIR) != 0)) { + printf("Error: output file is illegal.\n"); + isTrue &= false; + } else { + g_outputFile = optarg; + } + break; + } + case 'z': + g_compress = true; + break; + case 0: // long options + ParseLongOpt(argv[0], optIndex, isTrue); + break; + default: + ShowHelp(argv[0]); + isTrue &= false; + break; + } + return isTrue; +} + +static void IsInvalidOpt(int argc, char** argv) +{ + for (int i = optind; i < argc; i++) { + if (!IsTagSupported(argv[i])) { + fprintf(stderr, "Error: \"%s\" is not support category on this device.\n", argv[i]); + exit(0); + } + } +} + +static bool HandleOpt(int argc, char** argv) +{ + bool isTrue = true; + int opt = 0; + int optionIndex = 0; + string shortOption = "b:c:hlo:t:z"; + int argcSize = argc; + while (isTrue && argcSize-- > 0) { + opt = getopt_long(argc, argv, shortOption.c_str(), g_longOptions, &optionIndex); + if (opt < 0) { + IsInvalidOpt(argc, argv); + break; + } + isTrue &= ParseOpt(opt, argv, optionIndex); + } + return isTrue; +} + +static bool TruncateFile(const string& path) +{ + int fd = creat((g_traceRootPath + path).c_str(), 0); + if (fd == -1) { + fprintf(stderr, "Error: clear %s: %s (%d)\n", (g_traceRootPath + path).c_str(), + strerror(errno), errno); + return false; + } + close(fd); + fd = -1; + return true; +} + +static bool ClearTrace() +{ + return TruncateFile(TRACE_PATH); +} + +static bool StartTrace() +{ + if (!SetFtraceEnabled(TRACING_ON_PATH, true)) { + return false; + } + ClearTrace(); + printf("capturing trace...\n"); + fflush(stdout); + struct timespec ts = {0, 0}; + ts.tv_sec = g_traceDuration; + ts.tv_nsec = 0; + while ((nanosleep(&ts, &ts) == -1) && (errno == EINTR)) {} + return true; +} + +static bool StopTrace() +{ + return SetFtraceEnabled(TRACING_ON_PATH, false); +} + +static void DumpCompressedTrace(int traceFd, int outFd) +{ + z_stream zs { 0 }; + ssize_t bytesWritten; + ssize_t bytesRead; + if (memset_s(&zs, sizeof(zs), 0, sizeof(zs)) != 0) { + fprintf(stderr, "Error: zip stream buffer init failed."); + return; + } + int ret = deflateInit(&zs, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK) { + fprintf(stderr, "Error: initializing zlib: %d\n", ret); + return; + } + int have; + unique_ptr in = std::make_unique(CHUNK_SIZE); + unique_ptr out = std::make_unique(CHUNK_SIZE); + int flush = Z_NO_FLUSH; + if (!in || !out) { + fprintf(stderr, "Error: couldn't allocate buffers.\n"); + return; + } + do { + bytesRead = TEMP_FAILURE_RETRY(read(traceFd, in.get(), CHUNK_SIZE)); + if (bytesRead == 0) { + flush = Z_FINISH; + } else if (bytesRead == -1) { + fprintf(stderr, "Error: reading trace: %s (%d)\n", strerror(errno), errno); + break; + } + zs.next_in = reinterpret_cast(in.get()); + zs.avail_in = bytesRead; + do { + zs.next_out = reinterpret_cast(out.get()); + zs.avail_out = CHUNK_SIZE; + ret = deflate(&zs, flush); + if (ret != Z_OK) { + fprintf(stderr, "Error: deflate zlib: %d\n", ret); + break; + } + have = CHUNK_SIZE - zs.avail_out; + bytesWritten = TEMP_FAILURE_RETRY(write(outFd, out.get(), have)); + if (bytesWritten < 0 || (static_cast(bytesWritten) < static_cast(have)) || + bytesWritten == -1) { + fprintf(stderr, "Error: writing deflated trace: %s (%d)\n", strerror(errno), errno); + break; + } + } while (zs.avail_out == 0); + } while (flush != Z_FINISH); + deflateEnd(&zs); +} + +static void DumpTrace(int outFd, const string& path) +{ + int traceFd = open((g_traceRootPath + path).c_str(), O_RDWR); + if (traceFd == -1) { + fprintf(stderr, "error opening %s: %s (%d)\n", path.c_str(), + strerror(errno), errno); + return; + } + ssize_t bytesWritten; + ssize_t bytesRead; + if (g_compress) { + DumpCompressedTrace(traceFd, outFd); + } else { + char buffer[BLOCK_SIZE]; + do { + bytesRead = TEMP_FAILURE_RETRY(read(traceFd, buffer, BLOCK_SIZE)); + if ((bytesRead == 0) || (bytesRead == -1)) { + break; + } + bytesWritten = TEMP_FAILURE_RETRY(write(outFd, buffer, bytesRead)); + } while (bytesWritten > 0); + } + close(traceFd); +} + +static bool MarkOthersClockSync() +{ + constexpr unsigned int bufferSize = 128; // buffer size + char buffer[bufferSize] = { 0 }; + int fd = open((g_traceRootPath + TRACE_MARKER_PATH).c_str(), O_WRONLY); + if (fd == -1) { + fprintf(stderr, "Error: opening %s: %s (%d)\n", TRACE_MARKER_PATH.c_str(), strerror(errno), errno); + return false; + } + + struct timespec mts = {0, 0}; + struct timespec rts = {0, 0}; + if (clock_gettime(CLOCK_REALTIME, &rts) == -1) { + fprintf(stderr, "Error: get realtime %s (%d)\n", strerror(errno), errno); + close(fd); + return false; + } else if (clock_gettime(CLOCK_MONOTONIC, &mts) == -1) { + fprintf(stderr, "Error: get parent_ts %s (%d)\n", strerror(errno), errno); + close(fd); + return false; + } + constexpr unsigned int nanoSeconds = 1000000000; // seconds converted to nanoseconds + constexpr unsigned int nanoToMill = 1000000; // millisecond converted to nanoseconds + constexpr float nanoToSecond = 1000000000.0f; // consistent with the ftrace timestamp format + int len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, + "trace_event_clock_sync: realtime_ts=%" PRId64 "\n", + static_cast((rts.tv_sec * nanoSeconds + rts.tv_nsec) / nanoToMill)); + if (len < 0) { + fprintf(stderr, "Error: entering data into buffer %s (%d)\n", strerror(errno), errno); + close(fd); + return false; + } + if (write(fd, buffer, len) < 0) { + fprintf(stderr, "Error: writing clock sync marker %s (%d)\n", strerror(errno), errno); + } + len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "trace_event_clock_sync: parent_ts=%f\n", + static_cast(((static_cast(mts.tv_sec)) * nanoSeconds + mts.tv_nsec) / nanoToSecond)); + if (len < 0) { + fprintf(stderr, "Error: entering data into buffer %s (%d)\n", strerror(errno), errno); + close(fd); + return false; + } + if (write(fd, buffer, len) < 0) { + fprintf(stderr, "Error: writing clock sync marker %s (%d)\n", strerror(errno), errno); + } + close(fd); + return true; +} + +static void InitDiskSupportTags() +{ + g_tagMap["disk"] = { "disk", "Disk I/O", 0, KERNEL, { + { "events/f2fs/f2fs_sync_file_enter/enable" }, + { "events/f2fs/f2fs_sync_file_exit/enable" }, + { "events/f2fs/f2fs_write_begin/enable" }, + { "events/f2fs/f2fs_write_end/enable" }, + { "events/ext4/ext4_da_write_begin/enable" }, + { "events/ext4/ext4_da_write_end/enable" }, + { "events/ext4/ext4_sync_file_enter/enable" }, + { "events/ext4/ext4_sync_file_exit/enable" }, + { "events/block/block_rq_issue/enable" }, + { "events/block/block_rq_complete/enable" }, + }}; + g_tagMap["mmc"] = { "mmc", "eMMC commands", 0, KERNEL, { + { "events/mmc/enable" }, + }}; +} + +static void InitHardwareSupportTags() +{ + g_tagMap["irq"] = { "irq", "IRQ Events", 0, KERNEL, { + { "events/irq/enable" }, + { "events/ipi/enable" }, + }}; + g_tagMap["irqoff"] = { "irqoff", "IRQ-disabled code section tracing", 0, KERNEL, { + { "events/preemptirq/irq_enable/enable" }, + { "events/preemptirq/irq_disable/enable" }, + }}; + InitDiskSupportTags(); + g_tagMap["i2c"] = { "i2c", "I2C Events", 0, KERNEL, { + { "events/i2c/enable" }, + { "events/i2c/i2c_read/enable" }, + { "events/i2c/i2c_write/enable" }, + { "events/i2c/i2c_result/enable" }, + { "events/i2c/i2c_reply/enable" }, + { "events/i2c/smbus_read/enable" }, + { "events/i2c/smbus_write/enable" }, + { "events/i2c/smbus_result/enable" }, + { "events/i2c/smbus_reply/enable" }, + }}; + g_tagMap["freq"] = { "freq", "CPU Frequency", 0, KERNEL, { + { "events/power/cpu_frequency/enable" }, + { "events/power/clock_set_rate/enable" }, + { "events/power/clock_disable/enable" }, + { "events/power/clock_enable/enable" }, + { "events/clk/clk_set_rate/enable" }, + { "events/clk/clk_disable/enable" }, + { "events/clk/clk_enable/enable" }, + { "events/power/cpu_frequency_limits/enable" }, + }}; + g_tagMap["ufs"] = { "ufs", "UFS commands", 0, KERNEL, { + { "events/ufs/enable" }, + }}; + g_tagMap["regulators"] = { "regulators", "Voltage and Current Regulators", 0, KERNEL, { + { "events/regulator/enable" }, + }}; + g_tagMap["membus"] = { "membus", "Memory Bus Utilization", 0, KERNEL, { + { "events/memory_bus/enable" }, + }}; +} + +static void InitKernelSupportTags() +{ + g_tagMap["sched"] = { "sched", "CPU Scheduling", 0, KERNEL, { + { "events/sched/sched_switch/enable" }, + { "events/sched/sched_wakeup/enable" }, + { "events/sched/sched_wakeup_new/enable" }, + { "events/sched/sched_waking/enable" }, + { "events/sched/sched_blocked_reason/enable" }, + { "events/sched/sched_pi_setprio/enable" }, + { "events/sched/sched_process_exit/enable" }, + { "events/cgroup/enable" }, + { "events/oom/oom_score_adj_update/enable" }, + { "events/task/task_rename/enable" }, + { "events/task/task_newtask/enable" }, + }}; + g_tagMap["preemptoff"] = { "preemptoff", "Preempt-disabled code section tracing", 0, KERNEL, { + { "events/preemptirq/preempt_enable/enable" }, + { "events/preemptirq/preempt_disable/enable" }, + }}; + g_tagMap["idle"] = { "idle", "CPU Idle", 0, KERNEL, { + { "events/power/cpu_idle/enable" }, + }}; + + g_tagMap["load"] = { "load", "CPU Load", 0, KERNEL, { + { "events/cpufreq_interactive/enable" }, + }}; + g_tagMap["sync"] = { "sync", "Synchronization", 0, KERNEL, { + // linux kernel > 4.9 + { "events/dma_fence/enable" }, + }}; + g_tagMap["workq"] = { "workq", "Kernel Workqueues", 0, KERNEL, { + { "events/workqueue/enable" }, + }}; + g_tagMap["memreclaim"] = { "memreclaim", "Kernel Memory Reclaim", 0, KERNEL, { + { "events/vmscan/mm_vmscan_direct_reclaim_begin/enable" }, + { "events/vmscan/mm_vmscan_direct_reclaim_end/enable" }, + { "events/vmscan/mm_vmscan_kswapd_wake/enable" }, + { "events/vmscan/mm_vmscan_kswapd_sleep/enable" }, + { "events/lowmemorykiller/enable" }, + }}; + g_tagMap["pagecache"] = { "pagecache", "Page cache", 0, KERNEL, { + { "events/filemap/enable" }, + }}; + g_tagMap["memory"] = { "memory", "Memory", 0, KERNEL, { + { "events/kmem/rss_stat/enable" }, + { "events/kmem/ion_heap_grow/enable" }, + { "events/kmem/ion_heap_shrink/enable" }, + }}; + InitHardwareSupportTags(); +} + +static void InitAllSupportTags() +{ + // OHOS + g_tagMap["ohos"] = { "ohos", "OpenHarmony", BYTRACE_TAG_OHOS, USER, {}}; + g_tagMap["ability"] = { "ability", "Ability Manager", BYTRACE_TAG_ABILITY_MANAGER, USER, {}}; + g_tagMap["zcamera"] = { "zcamera", "OpenHarmony Camera Module", BYTRACE_TAG_ZCAMERA, USER, {}}; + g_tagMap["zmedia"] = { "zmedia", "OpenHarmony Media Module", BYTRACE_TAG_ZMEDIA, USER, {}}; + g_tagMap["zimage"] = { "zimage", "OpenHarmony Image Module", BYTRACE_TAG_ZIMAGE, USER, {}}; + g_tagMap["zaudio"] = { "zaudio", "OpenHarmony Audio Module", BYTRACE_TAG_ZAUDIO, USER, {}}; + g_tagMap["distributeddatamgr"] = { "distributeddatamgr", "Distributed Data Manager", + BYTRACE_TAG_DISTRIBUTEDDATA, USER, {}}; + g_tagMap["mdfs"] = { "mdfs", "Mobile Distributed File System", BYTRACE_TAG_MDFS, USER, {}}; + g_tagMap["graphic"] = { "graphic", "Graphic Module", BYTRACE_TAG_GRAPHIC_AGP, USER, {}}; + g_tagMap["ace"] = { "ace", "ACE development framework", BYTRACE_TAG_ACE, USER, {}}; + g_tagMap["notification"] = { "notification", "Notification Module", BYTRACE_TAG_NOTIFICATION, USER, {}}; + g_tagMap["app"] = { "app", "APP Module", BYTRACE_TAG_APP, USER, {}}; + g_tagMap["zbinder"] = { "zbinder", "Harmony binder communication", 0, KERNEL, { + { "events/zbinder/enable" }, + }}; + + // Kernel os + InitKernelSupportTags(); +} + +static void InterruptExit(int signo) +{ + _exit(-1); +} + +int main(int argc, char **argv) +{ + signal(SIGKILL, InterruptExit); + signal(SIGINT, InterruptExit); + + if (!IsTraceMounted()) { + exit(-1); + } + + InitAllSupportTags(); + + if (!HandleOpt(argc, argv)) { + exit(-1); + } + + if (!SetKernelSpaceSettings()) { + ClearKernelSpaceSettings(); + exit(-1); + } + + if (!SetUserSpaceSettings()) { + ClearKernelSpaceSettings(); + ClearUserSpaceSettings(); + exit(-1); + } + + bool isTrue = true; + + if (g_traceStart) { + SetViewStyle(); + isTrue &= StartTrace(); + } + + isTrue &= MarkOthersClockSync(); + + if (g_traceStop) { + isTrue &= StopTrace(); + } + + if (isTrue && g_traceDump) { + int outFd = STDOUT_FILENO; + if (g_outputFile.size() > 0) { + printf("write trace to %s\n", g_outputFile.c_str()); + outFd = open(g_outputFile.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + } + if (outFd == -1) { + printf("Failed to open '%s', err=%d", g_outputFile.c_str(), errno); + } else { + dprintf(outFd, "TRACE:\n"); + DumpTrace(outFd, TRACE_PATH); + if (outFd != STDOUT_FILENO) { + close(outFd); + outFd = -1; + } + } + ClearTrace(); + } + + ClearUserSpaceSettings(); + ClearKernelSpaceSettings(); + + return isTrue; +} diff --git a/bin/src/bytrace_capture.cpp b/bin/src/bytrace_capture.cpp new file mode 100755 index 0000000..62d97fb --- /dev/null +++ b/bin/src/bytrace_capture.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * 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 "bytrace_capture.h" +#include "bytrace.h" +#include "parameters.h" + +using namespace std; +bool SetPropertyInner(const string& property, const string& value) +{ + bool result = OHOS::system::SetParameter(property, value); + if (!result) { + fprintf(stderr, "Error: Failed to set %s property.\n", property.c_str()); + } + return result; +} + +std::string GetPropertyInner(const std::string& property, const std::string& value) +{ + return OHOS::system::GetParameter(property, value); +} + +void RefreshBinderServices() +{ +} + +bool RefreshHalServices() +{ + return true; +} diff --git a/bin/src/bytrace_impl.cpp b/bin/src/bytrace_impl.cpp new file mode 100755 index 0000000..59f5629 --- /dev/null +++ b/bin/src/bytrace_impl.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "bytrace.h" +#include "hilog/log.h" +#include "parameters.h" + +using namespace std; +using namespace OHOS::HiviewDFX; + +#define EXPECTANTLY(exp) (__builtin_expect(!!(exp), true)) +#define UNEXPECTANTLY(exp) (__builtin_expect(!!(exp), false)) + +namespace { +int g_markerFd = -1; +std::once_flag g_onceFlag; + +std::atomic g_isBytraceInit(false); +std::atomic g_tagsProperty(BYTRACE_TAG_NOT_READY); + +const std::string KEY_TRACE_TAG = "debug.bytrace.tags.enableflags"; +const std::string KEY_APP_NUMBER = "debug.bytrace.app_number"; +const std::string KEY_RO_DEBUGGABLE = "ro.debuggable"; + +constexpr int NAME_MAX_SIZE = 1000; +static std::vector g_markTypes = {"B", "E", "S", "F", "C"}; +enum MarkerType { MARKER_BEGIN, MARKER_END, MARKER_ASYNC_BEGIN, MARKER_ASYNC_END, MARKER_INT, MARKER_MAX }; + +bool IsAppValid() +{ + // Judge if application-level tracing is enabled. + if (OHOS::system::GetBoolParameter(KEY_RO_DEBUGGABLE, 0)) { + std::ifstream fs; + fs.open("/proc/self/cmdline"); + if (!fs.is_open()) { + fprintf(stderr, "IsAppValid, open /proc/self/cmdline failed.\n"); + return false; + } + + std::string lineStr; + std::getline(fs, lineStr); + std::string keyPrefix = "debug.bytrace.app_"; + int nums = OHOS::system::GetIntParameter(KEY_APP_NUMBER, 0); + for (int i = 0; i < nums; i++) { + std::string keyStr = keyPrefix + std::to_string(i); + std::string val = OHOS::system::GetParameter(keyStr, ""); + if (val == "*" || val == lineStr) { + fs.close(); + return true; + } + } + } + return false; +} + +uint64_t GetSysParamTags() +{ + // Get the system parameters of KEY_TRACE_TAG. + std::string tagStr = OHOS::system::GetParameter(KEY_TRACE_TAG, ""); + uint64_t tags = std::stoull(tagStr); + if (tagStr == "" || tags == ULLONG_MAX) { + fprintf(stderr, "GetSysParamTags error tag: %s.\n", tagStr.c_str()); + return 0; + } + + tags = IsAppValid() ? (tags | BYTRACE_TAG_APP) : (tags & (~BYTRACE_TAG_APP)); + return (tags | BYTRACE_TAG_ALWAYS) & BYTRACE_TAG_VALID_MASK; +} + +// open file "trace_marker". +void OpenTraceMarkerFile() +{ + const std::string debugFile = "/sys/kernel/debug/tracing/trace_marker"; + const std::string traceFile = "/sys/kernel/tracing/trace_marker"; + g_markerFd = open(debugFile.c_str(), O_WRONLY | O_CLOEXEC); + if (g_markerFd == -1) { + g_markerFd = open(traceFile.c_str(), O_WRONLY | O_CLOEXEC); + if (g_markerFd == -1) { + fprintf(stderr, "Error opening trace file.\n"); + g_tagsProperty = 0; + return; + } + } + g_tagsProperty = GetSysParamTags(); + g_isBytraceInit = true; +} +}; // namespace + +inline void AddBytraceMarker(MarkerType type, uint64_t tag, std::string name, std::string value) +{ + if (UNEXPECTANTLY(!g_isBytraceInit)) { + std::call_once(g_onceFlag, OpenTraceMarkerFile); + } + if (UNEXPECTANTLY(g_tagsProperty & tag)) { + // record fomart: "type|pid|name value". + std::string record = g_markTypes[type] + "|"; + record += std::to_string(getpid()) + "|"; + record += (name.size() < NAME_MAX_SIZE) ? name : name.substr(0, NAME_MAX_SIZE); + record += " " + value; + write(g_markerFd, record.c_str(), record.size()); + } +} + +void UpdateTraceLabel() +{ + if (!g_isBytraceInit) { + return; + } + g_tagsProperty = GetSysParamTags(); +} + +void StartTrace(uint64_t label, const string& value, float limit) +{ + string traceName = "H:" + value; + AddBytraceMarker(MARKER_BEGIN, label, traceName, ""); +} + +void StartTraceDebug(uint64_t label, const string& value, float limit) +{ +#if (TRACE_LEVEL >= DEBUG_LEVEL) + string traceName = "H:" + value + GetHiTraceInfo(); + AddBytraceMarker(MARKER_BEGIN, label, traceName, ""); +#endif +} + +void FinishTrace(uint64_t label, const string& value) +{ + AddBytraceMarker(MARKER_END, label, "", ""); +} + +void FinishTraceDebug(uint64_t label, const string& value) +{ +#if (TRACE_LEVEL >= DEBUG_LEVEL) + AddBytraceMarker(MARKER_END, label, "", ""); +#endif +} + +void StartAsyncTrace(uint64_t label, const string& value, int32_t taskId, float limit) +{ + string traceName = "H:" + value; + AddBytraceMarker(MARKER_ASYNC_BEGIN, label, traceName, std::to_string(taskId)); +} + +void StartAsyncTraceDebug(uint64_t label, const string& value, int32_t taskId, float limit) +{ +#if (TRACE_LEVEL >= DEBUG_LEVEL) + string traceName = "H:" + value; + AddBytraceMarker(MARKER_ASYNC_BEGIN, label, traceName, std::to_string(taskId)); +#endif +} + +void FinishAsyncTrace(uint64_t label, const string& value, int32_t taskId) +{ + string traceName = "H:" + value; + AddBytraceMarker(MARKER_ASYNC_END, label, traceName, std::to_string(taskId)); +} + +void FinishAsyncTraceDebug(uint64_t label, const string& value, int32_t taskId) +{ +#if (TRACE_LEVEL >= DEBUG_LEVEL) + string traceName = "H:" + value; + AddBytraceMarker(MARKER_ASYNC_END, label, traceName, std::to_string(taskId)); +#endif +} + +void MiddleTrace(uint64_t label, const string& beforeValue, const std::string& afterValue) +{ + string traceName = "H:" + afterValue; + AddBytraceMarker(MARKER_END, label, "", ""); + AddBytraceMarker(MARKER_BEGIN, label, traceName, ""); +} + +void MiddleTraceDebug(uint64_t label, const string& beforeValue, const std::string& afterValue) +{ +#if (TRACE_LEVEL >= DEBUG_LEVEL) + string traceName = "H:" + afterValue + GetTraceInfo(); + AddBytraceMarker(MARKER_END, label, "", ""); + AddBytraceMarker(MARKER_BEGIN, label, traceName, ""); +#endif +} + +void CountTrace(uint64_t label, const string& name, int64_t count) +{ + string traceName = "H:" + name; + AddBytraceMarker(MARKER_INT, label, traceName, std::to_string(count)); +} + +void CountTraceDebug(uint64_t label, const string& name, int64_t count) +{ +#if (TRACE_LEVEL >= DEBUG_LEVEL) + string traceName = "H:" + name; + AddBytraceMarker(MARKER_INT, label, traceName, std::to_string(count)); +#endif +} diff --git a/bin/test/BUILD.gn b/bin/test/BUILD.gn new file mode 100755 index 0000000..e726a93 --- /dev/null +++ b/bin/test/BUILD.gn @@ -0,0 +1,41 @@ +# Copyright (C) 2021 Huawei Device Co., Ltd. +# 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. + +import("//build/test.gni") +import("//developtools/bytrace_standard/bytrace.gni") + +module_output_path = "bytrace_standard/bytrace" + +config("module_private_config") { + visibility = [ ":*" ] +} + +ohos_moduletest("BytraceNDKTest") { + module_out_path = module_output_path + sources = [ "moduletest/common/native/bytrace_ndk_test.cpp" ] + deps = [ + "${bytrace_path}/bin:bytrace_capture_inner", + "${innerkits_path}/native:bytrace_core", + "//third_party/googletest:gtest_main", + ] + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "startup_l2:syspara", + ] + include_dirs = [ "${innerkits_path}/bytrace/bytrace_native/include" ] +} + +group("moduletest") { + testonly = true + deps = [ ":BytraceNDKTest" ] +} diff --git a/bin/test/moduletest/common/native/bytrace_ndk_test.cpp b/bin/test/moduletest/common/native/bytrace_ndk_test.cpp new file mode 100755 index 0000000..7d6bb1d --- /dev/null +++ b/bin/test/moduletest/common/native/bytrace_ndk_test.cpp @@ -0,0 +1,677 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * 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 +#include +#include +#include +#include +#include +#include "bytrace.h" +#include "bytrace_capture.h" +#include "parameters.h" + +using namespace testing::ext; +using namespace std; +using namespace OHOS::HiviewDFX; +namespace OHOS { +namespace Developtools { +namespace BytraceTest { +const string TRACE_MARKER_PATH = "trace_marker"; +const string TRACING_ON_PATH = "tracing_on"; +const string TRACING_ON = "tracing_on"; +const string TRACE_PATH = "trace"; +const string TRACE_MARK_WRITE = "tracing_mark_write"; +const string TRACE_PATTERN = "\\s*(.*?)-(.*?)\\s+(.*?)\\[(\\d+?)\\]\\s+(.*?)\\s+((\\d+).(\\d+)?):\\s+" + + TRACE_MARK_WRITE + ": "; +const string TRACE_START = TRACE_PATTERN + "B\\|(.*?)\\|H:"; +const string TRACE_FINISH = TRACE_PATTERN + "E\\|"; +const string TRACE_ASYNC_START = TRACE_PATTERN + "S\\|(.*?)\\|H:"; +const string TRACE_ASYNC_FINISH = TRACE_PATTERN + "F\\|(.*?)\\|H:"; +const string TRACE_COUNT = TRACE_PATTERN + "C\\|(.*?)\\|H:"; +const string TRACE_PROPERTY = "debug.bytrace.tags.enableflags"; +constexpr uint32_t TASK = 1; +constexpr uint32_t TID = 2; +constexpr uint32_t TGID = 3; +constexpr uint32_t CPU = 4; +constexpr uint32_t DNH2 = 5; +constexpr uint32_t TIMESTAMP = 6; +constexpr uint32_t PID = 9; +constexpr uint32_t TRACE_NAME = 10; +constexpr uint32_t NUM = 11; + +constexpr uint32_t TRACE_FMA11 = 11; +constexpr uint32_t TRACE_FMA12 = 12; + +constexpr uint64_t TRACE_INVALIDATE_TAG = 0x1000000; +constexpr uint64_t BYTRACE_TAG = 0xd03301; +const constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, BYTRACE_TAG, "BYTRACE_TEST"}; +const uint64_t TAG = BYTRACE_TAG_OHOS; +static string g_traceRootPath; + +bool SetProperty(const string& property, const string& value); +string GetProperty(const string& property, const string& value); +bool CleanTrace(); +bool CleanFtrace(); +bool SetFtrace(const string& filename, bool enabled); + +class BytraceNDKTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(){}; +}; + +void BytraceNDKTest::SetUpTestCase() +{ + const string debugfsDir = "/sys/kernel/debug/tracing/"; + const string tracefsDir = "/sys/kernel/tracing/"; + if (access((debugfsDir + TRACE_MARKER_PATH).c_str(), F_OK) != -1) { + g_traceRootPath = debugfsDir; + } else if (access((tracefsDir + TRACE_MARKER_PATH).c_str(), F_OK) != -1) { + g_traceRootPath = tracefsDir; + } else { + HiLog::Error(LABEL, "Error: Finding trace folder failed"); + } + CleanFtrace(); +} + +void BytraceNDKTest::TearDownTestCase() +{ + SetProperty(TRACE_PROPERTY, "0"); + SetFtrace(TRACING_ON, false); + CleanTrace(); +} + +void BytraceNDKTest::SetUp() +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + string value = to_string(TAG); + SetProperty(TRACE_PROPERTY, value); + HiLog::Info(LABEL, "current tag is %{public}s", GetProperty(TRACE_PROPERTY, "0").c_str()); + ASSERT_TRUE(GetProperty(TRACE_PROPERTY, "-123") == value); + UpdateTraceLabel(); +} + +struct Param { + string m_task; + string m_tid; + string m_tgid; + string m_cpu; + string m_dnh2; + string m_timestamp; + string m_pid; + string m_traceName; + string m_num; +}; + +class MyTrace { + Param m_param; + bool m_loaded; +public: + MyTrace() : m_loaded(false) + { + m_param.m_task = ""; + m_param.m_tid = ""; + m_param.m_tgid = ""; + m_param.m_cpu = ""; + m_param.m_dnh2 = ""; + m_param.m_timestamp = ""; + m_param.m_pid = ""; + m_param.m_traceName = ""; + m_param.m_num = ""; + } + + ~MyTrace() + { + } + + // task-pid ( tig) [cpu] ...1 timestamp: tracing_mark_write: B|pid|traceName + // task-pid ( tig) [cpu] ...1 timestamp: tracing_mark_write: E|pid + void Load(const Param& param) + { + m_param.m_task = param.m_task; + m_param.m_pid = param.m_pid; + m_param.m_tid = param.m_tid; + m_param.m_tgid = param.m_tgid; + m_param.m_cpu = param.m_cpu; + m_param.m_dnh2 = param.m_dnh2; + m_param.m_timestamp = param.m_timestamp; + m_param.m_traceName = param.m_traceName; + m_param.m_num = param.m_num; + m_loaded = true; + } + + string GetTask() + { + return m_param.m_task; + } + + string GetPid() + { + if (m_loaded) { + return m_param.m_pid; + } + return ""; + } + + string GetTgid() + { + if (m_loaded) { + return m_param.m_tgid; + } + return ""; + } + + string GetCpu() + { + if (m_loaded) { + return m_param.m_cpu; + } + return ""; + } + + string GetDnh2() + { + if (m_loaded) { + return m_param.m_dnh2; + } + return ""; + } + + string GetTimestamp() + { + if (m_loaded) { + return m_param.m_timestamp; + } + return ""; + } + + string GetTraceName() + { + if (m_loaded) { + return m_param.m_traceName; + } + return ""; + } + + string GetNum() + { + if (m_loaded) { + return m_param.m_num; + } + return ""; + } + + string GetTid() + { + if (m_loaded) { + return m_param.m_tid; + } + return ""; + } + + bool IsLoaded() const + { + return m_loaded; + } +}; + +bool SetProperty(const string& property, const string& value) +{ + bool result = false; + result = OHOS::system::SetParameter(property, value); + if (!result) { + HiLog::Error(LABEL, "Error: setting %s failed", property.c_str()); + return false; + } + return true; +} + +string GetProperty(const string& property, const string& value) +{ + return OHOS::system::GetParameter(property, value); +} + +bool GetTimeDuration(int64_t time1, int64_t time2, int64_t diffRange) +{ + int64_t duration = time2 - time1; + return (duration > 0) && (duration <= diffRange ? true : false); +} + +string& Trim(string& s) +{ + if (s.empty()) { + return s; + } + s.erase(0, s.find_first_not_of(" ")); + s.erase(s.find_last_not_of(" ") + 1); + return s; +} + +int64_t GetTimeStamp(string str) +{ + if (str == "") { + return 0; + } + int64_t time; + Trim(str); + time = atol(str.erase(str.find("."), 1).c_str()); + return time; +} + +MyTrace GetTraceResult(const string& checkContent, const vector& list) +{ + MyTrace trace; + if (list.empty() || checkContent == "") { + return trace; + } + regex pattern(checkContent); + smatch match; + Param param {""}; + for (int i = list.size() - 1; i >= 0; i--) { + if (regex_match(list[i], match, pattern)) { + param.m_task = match[TASK]; + param.m_tid = match[TID]; + param.m_tgid = match[TGID]; + param.m_cpu = match[CPU]; + param.m_dnh2 = match[DNH2]; + param.m_timestamp = match[TIMESTAMP]; + param.m_pid = match[PID]; + if (match.size() == TRACE_FMA11) { + param.m_traceName = match[TRACE_NAME], + param.m_num = ""; + } else if (match.size() == TRACE_FMA12) { + param.m_traceName = match[TRACE_NAME], + param.m_num = match[NUM]; + } else { + param.m_traceName = ""; + param.m_num = ""; + } + trace.Load(param); + break; + } + } + return trace; +} + +static bool WriteStringToFile(const string& fileName, const string& str) +{ + if (g_traceRootPath.empty()) { + HiLog::Error(LABEL, "Error: trace path not found."); + return false; + } + ofstream out; + out.open(g_traceRootPath + fileName, ios::out); + out << str; + out.close(); + return true; +} + +bool CleanTrace() +{ + if (g_traceRootPath.empty()) { + HiLog::Error(LABEL, "Error: trace path not found."); + return false; + } + ofstream ofs; + ofs.open(g_traceRootPath + TRACE_PATH, ofstream::out); + if (!ofs.is_open()) { + HiLog::Error(LABEL, "Error: opening trace path failed."); + return false; + } + ofs << ""; + ofs.close(); + return true; +} + +static stringstream ReadFile(const string& filename) +{ + ifstream fin(filename.c_str()); + stringstream ss; + if (!fin.is_open()) { + fprintf(stderr, "opening file: %s failed!", filename.c_str()); + return ss; + } + ss << fin.rdbuf(); + fin.close(); + return ss; +} + +static bool IsFileExisting(const string& filename) +{ + return access(filename.c_str(), F_OK) != -1; +} + +bool SetFtrace(const string& filename, bool enabled) +{ + return WriteStringToFile(filename, enabled ? "1" : "0"); +} + +bool CleanFtrace() +{ + return WriteStringToFile("set_event", ""); +} + +string GetFinishTraceRegex(MyTrace& trace) +{ + if (!trace.IsLoaded()) { + return ""; + } else { + return "\\s*(.*?)-(" + trace.GetTid() + "?)\\s+(.*?)\\[(\\d+?)\\]\\s+(.*?)\\s+" + "((\\d+).(\\d+)?):\\s+" + + TRACE_MARK_WRITE + ": E\\|(" + trace.GetPid() + ")|(.*)"; + } +} + +vector ReadFile2string(const string& filename) +{ + vector list; + if (IsFileExisting(filename)) { + stringstream ss = ReadFile(filename); + string line; + while (getline(ss, line)) { + list.emplace_back(move(line)); + } + } + return list; +} + +vector ReadTrace() +{ + return ReadFile2string(g_traceRootPath + TRACE_PATH); +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node normal output start tracing and end tracing. + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_001, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartTrace(TAG, "StartTraceTest001"); + FinishTrace(TAG, "StartTraceTest001"); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + vector list = ReadTrace(); + MyTrace startTrace = GetTraceResult(TRACE_START + "(StartTraceTest001) ", list); + ASSERT_TRUE(startTrace.IsLoaded()) << "Can't find \"B|pid|StartTraceTest001\" from trace."; + MyTrace finishTrace = GetTraceResult(GetFinishTraceRegex(startTrace), list); + ASSERT_TRUE(finishTrace.IsLoaded()) << "Can't find \"E|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node has no output. + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_002, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartTrace(TAG, "StartTraceTest002"); + FinishTrace(TAG, "StartTraceTest002"); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + vector list = ReadTrace(); + MyTrace startTrace = GetTraceResult(TRACE_START + "(StartTraceTest002) ", list); + ASSERT_TRUE(startTrace.IsLoaded()) << "Can't find \"B|pid|StartTraceTest002\" from trace."; + MyTrace finishTrace = GetTraceResult(GetFinishTraceRegex(startTrace), list); + ASSERT_TRUE(finishTrace.IsLoaded()) << "Can't find \"E|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node normal output start trace and end trace. + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_003, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartTrace(TAG, "StartTraceTest003 %s"); + FinishTrace(TAG, "StartTraceTest003 %s"); + vector list = ReadTrace(); + MyTrace startTrace = GetTraceResult(TRACE_START + "(StartTraceTest003 %s) ", list); + ASSERT_TRUE(startTrace.IsLoaded()) << "Can't find \"B|pid|StartTraceTest003 %s\" from trace."; + MyTrace finishTrace = GetTraceResult(GetFinishTraceRegex(startTrace), list); + ASSERT_TRUE(finishTrace.IsLoaded()) << "Can't find \"E|\" from trace."; + ASSERT_TRUE(CleanTrace()); + list.clear(); + StartTrace(TAG, "StartTraceTest003 %p"); + FinishTrace(TAG, "StartTraceTest003 %p"); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + list = ReadTrace(); + MyTrace startTrace2 = GetTraceResult(TRACE_START + "(StartTraceTest003 %p) ", list); + MyTrace finishTrace2 = GetTraceResult(GetFinishTraceRegex(startTrace), list); + ASSERT_TRUE(finishTrace2.IsLoaded()) << "Can't find \"E|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: test Input and output interval 1ms execution, time fluctuation 1ms + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_004, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartTrace(TAG, "StartTraceTest004"); + usleep(1000); + FinishTrace(TAG, "StartTraceTest004"); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + vector list = ReadTrace(); + MyTrace startTrace = GetTraceResult(TRACE_START + "(StartTraceTest004) ", list); + ASSERT_TRUE(startTrace.IsLoaded()) << "Can't find \"B|pid|StartTraceTest004\" from trace."; + MyTrace finishTrace = GetTraceResult(GetFinishTraceRegex(startTrace), list); + ASSERT_TRUE(finishTrace.IsLoaded()) << "Can't find \"E|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node normal output start trace and end trace + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_005, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartAsyncTrace(TAG, "asyncTraceTest005", 123); + FinishAsyncTrace(TAG, "asyncTraceTest005", 123); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + vector list = ReadTrace(); + MyTrace startTrace = GetTraceResult(TRACE_ASYNC_START + "(asyncTraceTest005) (.*)", list); + ASSERT_TRUE(startTrace.IsLoaded()) << "Can't find \"S|pid|asyncTraceTest005\" from trace."; + MyTrace finishTrace = + GetTraceResult(TRACE_ASYNC_FINISH + startTrace.GetTraceName() + " " + startTrace.GetNum(), list); + ASSERT_TRUE(finishTrace.IsLoaded()) << "Can't find \"F|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node normal output start trace and end trace + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_006, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + CountTrace(TAG, "countTraceTest006", 1); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + vector list = ReadTrace(); + MyTrace countTrace = GetTraceResult(TRACE_COUNT + "(countTraceTest006) (.*)", list); + ASSERT_TRUE(countTrace.IsLoaded()) << "Can't find \"C|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node normal output start trace and end trace. + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_007, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartTrace(TRACE_INVALIDATE_TAG, "StartTraceTest007"); + FinishTrace(TRACE_INVALIDATE_TAG, "StartTraceTest007"); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + vector list = ReadTrace(); + MyTrace startTrace = GetTraceResult(TRACE_START + "(StartTraceTest007)", list); + EXPECT_FALSE(startTrace.IsLoaded()) << "Can't find \"B|pid|StartTraceTest007\" from trace."; + MyTrace finishTrace = GetTraceResult(GetFinishTraceRegex(startTrace), list); + EXPECT_FALSE(finishTrace.IsLoaded()) << "Can't find \"E|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node normal output start trace and end trace. + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_008, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartTrace(TRACE_INVALIDATE_TAG, "StartTraceTest008 %s"); + FinishTrace(TRACE_INVALIDATE_TAG, "StartTraceTest008 %s"); + vector list = ReadTrace(); + MyTrace startTrace = GetTraceResult(TRACE_START + "(StartTraceTest008 %s)", list); + EXPECT_FALSE(startTrace.IsLoaded()) << "Can't find \"B|pid|StartTraceTest008 %s\" from trace."; + MyTrace finishTrace = GetTraceResult(GetFinishTraceRegex(startTrace), list); + EXPECT_FALSE(finishTrace.IsLoaded()) << "Can't find \"E|\" from trace."; + ASSERT_TRUE(CleanTrace()); + list.clear(); + StartTrace(TRACE_INVALIDATE_TAG, "StartTraceTest008 %p"); + FinishTrace(TRACE_INVALIDATE_TAG, "StartTraceTest008 %p"); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + list = ReadTrace(); + MyTrace startTrace2 = GetTraceResult(TRACE_START + "(StartTraceTest008 %p)", list); + MyTrace finishTrace2 = GetTraceResult(GetFinishTraceRegex(startTrace), list); + EXPECT_FALSE(finishTrace2.IsLoaded()) << "Can't find \"E|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node normal output start trace and end trace + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_009, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartAsyncTrace(TRACE_INVALIDATE_TAG, "asyncTraceTest009", 123); + FinishAsyncTrace(TRACE_INVALIDATE_TAG, "asyncTraceTest009", 123); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + vector list = ReadTrace(); + MyTrace startTrace = GetTraceResult(TRACE_ASYNC_START + "(asyncTraceTest009)\\|(.*)", list); + EXPECT_FALSE(startTrace.IsLoaded()) << "Can't find \"S|pid|asyncTraceTest009\" from trace."; + MyTrace finishTrace = GetTraceResult(TRACE_ASYNC_FINISH + startTrace.GetTraceName() + "\\|" + + startTrace.GetNum(), list); + EXPECT_FALSE(finishTrace.IsLoaded()) << "Can't find \"F|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node normal output start trace and end trace + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_010, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + CountTrace(TRACE_INVALIDATE_TAG, "countTraceTest010", 1); + ASSERT_TRUE(SetFtrace(TRACING_ON, false)) << "Setting tracing_on failed."; + vector list = ReadTrace(); + MyTrace countTrace = GetTraceResult(TRACE_COUNT + "(countTraceTest010)\\|(.*)", list); + EXPECT_FALSE(countTrace.IsLoaded()) << "Can't find \"C|\" from trace."; +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node general output start and end tracing for debugging. + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_011, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartTraceDebug(TAG, "StartTraceTest011"); + FinishTraceDebug(TAG, "StartTraceTest011"); +} + +/** + * @tc.name: bytrace + * @tc.desc: tracing_mark_write file node general output start and end tracing for debugging. + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_012, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartTraceDebug(TAG, "StartTraceTest012 %s"); + FinishTraceDebug(TAG, "StartTraceTest012 %s"); +} + +/** + * @tc.name: bytrace + * @tc.desc: Testing StartAsyncTraceDebug and FinishAsyncTraceDebug functions + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_013, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + StartAsyncTraceDebug(TAG, "asyncTraceTest013", 123); + FinishAsyncTraceDebug(TAG, "asyncTraceTest013", 123); +} + +/** + * @tc.name: bytrace + * @tc.desc: Testing CountTraceDebug function + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_014, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + CountTraceDebug(TAG, "countTraceTest014", 1); +} + +/** + * @tc.name: bytrace + * @tc.desc: Testing MiddleTrace function + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_015, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + MiddleTrace(TAG, "MiddleTraceTest015", "050tseTecarTelddiM"); +} + +/** + * @tc.name: bytrace + * @tc.desc: Testing MiddleTraceDebug function + * @tc.type: FUNC + */ +HWTEST_F(BytraceNDKTest, StartTrace_016, TestSize.Level1) +{ + ASSERT_TRUE(CleanTrace()); + ASSERT_TRUE(SetFtrace(TRACING_ON, true)) << "Setting tracing_on failed."; + MiddleTraceDebug(TAG, "MiddleTraceTest016", "061tseTecarTelddiM"); +} +} // namespace BytraceTest +} // namespace Developtools +} // namespace OHOS diff --git a/bytrace.gni b/bytrace.gni new file mode 100755 index 0000000..c91cf8b --- /dev/null +++ b/bytrace.gni @@ -0,0 +1,18 @@ +# Copyright (C) 2021 Huawei Device Co., Ltd. +# 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. + +bytrace_path = "//developtools/bytrace_standard" + +innerkits_path = "${bytrace_path}/interfaces/innerkits" + +kits_path = "${bytrace_path}/interfaces/kits" diff --git a/figures/20210422-202314(WeLinkPC).png b/figures/20210422-202314(WeLinkPC).png new file mode 100755 index 0000000..6d3b9c0 Binary files /dev/null and b/figures/20210422-202314(WeLinkPC).png differ diff --git a/interfaces/innerkits/BUILD.gn b/interfaces/innerkits/BUILD.gn new file mode 100755 index 0000000..4c9f44a --- /dev/null +++ b/interfaces/innerkits/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (C) 2021 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") + +group("innerkits_target") { + deps = [ "native:bytrace_core" ] +} diff --git a/interfaces/innerkits/native/BUILD.gn b/interfaces/innerkits/native/BUILD.gn new file mode 100755 index 0000000..46eb8b9 --- /dev/null +++ b/interfaces/innerkits/native/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright (C) 2021 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") +import("//developtools/bytrace_standard/bytrace.gni") + +config("bytrace_config") { + visibility = [ ":*" ] + include_dirs = [ "include" ] +} + +ohos_shared_library("bytrace_core") { + public_configs = [ ":bytrace_config" ] + deps = [ "${bytrace_path}/bin:bytrace_inner" ] + subsystem_name = "developtools" + part_name = "bytrace_standard" +} diff --git a/interfaces/innerkits/native/include/bytrace.h b/interfaces/innerkits/native/include/bytrace.h new file mode 100755 index 0000000..b2e05a7 --- /dev/null +++ b/interfaces/innerkits/native/include/bytrace.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * 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 DEVELOPTOOLS_INTERFACES_INNERKITS_BYTRACE_INCLUDE_BYTRACE_H +#define DEVELOPTOOLS_INTERFACES_INNERKITS_BYTRACE_INCLUDE_BYTRACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +__BEGIN_DECLS + +constexpr uint64_t BYTRACE_TAG_NEVER = 0; // This tag is never enabled. +constexpr uint64_t BYTRACE_TAG_ALWAYS = (1ULL << 0); // This tag is always enabled. +constexpr uint64_t BYTRACE_TAG_OHOS = (1ULL << 30); // OHOS generic tag. +constexpr uint64_t BYTRACE_TAG_ABILITY_MANAGER = (1ULL << 31); // Ability Manager tag. +constexpr uint64_t BYTRACE_TAG_ZCAMERA = (1ULL << 32); // Camera module tag. +constexpr uint64_t BYTRACE_TAG_ZMEDIA = (1ULL << 33); // Media module tag. +constexpr uint64_t BYTRACE_TAG_ZIMAGE = (1ULL << 34); // Image module tag. +constexpr uint64_t BYTRACE_TAG_ZAUDIO = (1ULL << 35); // Audio module tag. +constexpr uint64_t BYTRACE_TAG_DISTRIBUTEDDATA = (1ULL << 36); // Distributeddata manager module tag. +constexpr uint64_t BYTRACE_TAG_MDFS = (1ULL << 37); // Mobile distributed file system tag. +constexpr uint64_t BYTRACE_TAG_GRAPHIC_AGP = (1ULL << 38); // Graphic module tag. +constexpr uint64_t BYTRACE_TAG_ACE = (1ULL << 39); // ACE development framework tag. +constexpr uint64_t BYTRACE_TAG_NOTIFICATION = (1ULL << 40); // Notification module tag. +constexpr uint64_t BYTRACE_TAG_APP = (1ULL << 62); // App tag. + +constexpr uint64_t BYTRACE_TAG_LAST = BYTRACE_TAG_APP; +constexpr uint64_t BYTRACE_TAG_NOT_READY = (1ULL << 63); // Reserved for initialization. +constexpr uint64_t BYTRACE_TAG_VALID_MASK = ((BYTRACE_TAG_LAST - 1) | BYTRACE_TAG_LAST); + +#ifndef BYTRACE_TAG +#define BYTRACE_TAG BYTRACE_TAG_NEVER +#elif BYTRACE_TAG > BYTRACE_TAG_VALID_MASK +#error BYTRACE_TAG must be defined to be one of the tags defined in bytrace.h +#elif BYTRACE_TAG < BYTRACE_TAG_OHOS +#error BYTRACE_TAG must be defined to be one of the tags defined in bytrace.h +#endif + +#define RELEASE_LEVEL 0X01 +#define DEBUG_LEVEL 0X02 + +#ifndef TRACE_LEVEL +#define TRACE_LEVEL RELEASE +#endif + +/** + * Update trace label when your process has started. + */ +void UpdateTraceLabel(); + +/** + * Track the beginning of a context. + */ +void StartTrace(uint64_t label, const std::string& value, float limit = -1); +void StartTraceDebug(uint64_t label, const std::string& value, float limit = -1); +/** + * Track the end of a context. + */ +void FinishTrace(uint64_t label, const std::string& value); +void FinishTraceDebug(uint64_t label, const std::string& value); +/** + * Track the beginning of an asynchronous event. + */ +void StartAsyncTrace(uint64_t label, const std::string& value, int32_t taskId, float limit = -1); +void StartAsyncTraceDebug(uint64_t label, const std::string& value, int32_t taskId, float limit = -1); + +/** + * Track the end of an asynchronous event. + */ +void FinishAsyncTrace(uint64_t label, const std::string& value, int32_t taskId); +void FinishAsyncTraceDebug(uint64_t label, const std::string& value, int32_t taskId); + +/** + * Track the middle of a context. Match the previous function of StartTrace before it. + */ +void MiddleTrace(uint64_t label, const std::string& beforeValue, const std::string& afterValue); +void MiddleTraceDebug(uint64_t label, const std::string& beforeValue, const std::string& afterValue); + +/** + * Track the 64-bit integer counter value. + */ +void CountTrace(uint64_t label, const std::string& name, int64_t count); +void CountTraceDebug(uint64_t label, const std::string& name, int64_t count); + +__END_DECLS +#ifdef __cplusplus +} +#endif +#endif // DEVELOPTOOLS_INTERFACES_INNERKITS_BYTRACE_INCLUDE_BYTRACE_H diff --git a/interfaces/kits/js/@ohos.bytrace.d.ts b/interfaces/kits/js/@ohos.bytrace.d.ts new file mode 100644 index 0000000..8776352 --- /dev/null +++ b/interfaces/kits/js/@ohos.bytrace.d.ts @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * 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. + */ + +/** + * Provides interfaces to generate {@code ByTrace} logs. + * + *

This class traces the start, end, and value changes of key processes that last for at least 3 ms. + * + *

Example: + * To trace a name verification that is expected to complete within 5 ms: + *

{@code
+ * Bytrace.startTrace("checkName", "5");
+ * Bytrace.finishTrace("checkName");
+ * }
+ * To trace the number of layers, which is 3: + *
{@code
+ * Bytrace.count("curLayer", 3);
+ * }
+ * + *

Each {@code startTrace} matches one {@code finishTrace}, and they must have the same value. + * + * @since 1 + */ +declare namespace bytrace { + /** + * Updates the trace label when your process has started. + */ + function updateTraceLabel(): void; + + /** + * Records a trace with the expected completion time and marks it as the start of a task. + * + * This method is invoked at the start of a transaction to indicate that a task whose name is specified by + * {@code value} has started. The {@link #finishTrace(String)} method using the same {@code value} must be + * invoked at the end of the transaction. + * + * @param value Indicates the operation name. + * @param limit Indicates the expected time required for completing the operation, in milliseconds. + */ + function startTrace(value: string, limit: number): void; + + /** + * Track the middle of a context. Match the previous function of StartTrace before it. + * + * @param beforeValue Indicates the matched startTrace vlaue. + * @param afterValue Indicates the matched finishTrace value. + */ + function middleTrace(beforeValue: string, afterValue: string): void; + + /** + * Records a trace and marks it as the end of a task. + * + * This method is invoked at the end of a transaction to indicate that a task whose name is specified by + * {@code value} has ended. This method must be invoked after the {@link #startTrace(String, float)} or + * {@link #startTrace(String)} method is invoked. + * + * @param value Indicates the operation name. It must be the same as the {@code value} of + * {@link #startTrace(String, float)} or {@link #startTrace(String)}. + * + */ + function finishTrace(value: string): void; + + /** + * Writes a async trace message and mark as start spot. The name and cookie + * used to begin an event must be used to end it. + * + * @param value Message content. + * @param taskId Unique identifier for distinguishing simultaneous events. + * @param limit It is expected that the operation will be completed within the specified time period. + */ + function startAsyncTrace(value: string, taskId: number, limit: number): void; + + /** + * Writes a async trace message and mark as start spot. The name and cookie + * used to begin an event must be used to end it. + * + * @param value Message content. + * @param taskId Unique identifier for distinguishing simultaneous events. + */ + function finishAsyncTrace(value: string, taskId: number): void; + + /** + * Records a trace for generating a count, such as clock pulse and the number of layers. + * + * @param name Indicates the operation name. + * @param count Indicates the count of the operation. + */ + function countTrace(name: string, count: number): void; +} +export default bytrace; diff --git a/ohos.build b/ohos.build new file mode 100755 index 0000000..f898d94 --- /dev/null +++ b/ohos.build @@ -0,0 +1,26 @@ +{ + "subsystem": "developtools", + "parts": { + "bytrace_standard": { + "module_list": [ + "//developtools/bytrace_standard/interfaces/innerkits/native:bytrace_core", + "//developtools/bytrace_standard/bin:bytrace_target" + ], + "inner_kits": [ + { + "type": "so", + "name": "//developtools/bytrace_standard/interfaces/innerkits/native:bytrace_core", + "header": { + "header_files": [ + "bytrace.h" + ], + "header_base": "//developtools/bytrace_standard/interfaces/innerkits/native/include" + } + } + ], + "test_list": [ + "//developtools/bytrace_standard/bin/test:moduletest" + ] + } + } +} diff --git a/script/bytrace_capture.bat b/script/bytrace_capture.bat new file mode 100755 index 0000000..ce59ffe --- /dev/null +++ b/script/bytrace_capture.bat @@ -0,0 +1,21 @@ +@rem Copyright (C) 2021 Huawei Device Co., Ltd. +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. + +@echo off +hdc shell "echo > /sys/kernel/debug/tracing/trace" +hdc shell "echo 4096 > /d/tracing/saved_cmdlines_size" +hdc shell "bytrace -t 10 -b 4096 --overwrite ohos zimage zmedia zcamera zaudio ability distributeddatamgr sched freq irq workq rs idle load disk pagecache memreclaim > /data/mynewtrace.ftrace" +hdc shell "echo > /sys/kernel/debug/tracing/trace" +hdc shell "sed -i '1,2d' /data/mynewtrace.ftrace" +hdc pull /data/mynewtrace.ftrace . +pause diff --git a/script/bytrace_multi.py b/script/bytrace_multi.py new file mode 100755 index 0000000..3518fb0 --- /dev/null +++ b/script/bytrace_multi.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2021 Huawei Device Co., Ltd. +# 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. + +import codecs +import sys +import os +import re +import time + +files = [] +trace_regex = "\s*(.*?)-(\d+?)\s+\(\s*(\d+?)\)\s+\[\d+\]\s+(.*?)\s+(.*?):\s+" +all_traces_dict = {} # {"deviceName": [(timestamp, line) ...]} +all_real_time_dict = {} +default_real_time = 7983849599000 # 2222-12-31 23:59:59 + +def compare_timestamp(time1, time2): + return float(time1) < float(time2) + +def read_files(file, devices_name): + traces = [] + with codecs.open(file, 'r', encoding='utf-8') as fp: + for line in fp: + if line.find("binder_transaction") > -1 \ + or line.find("tracing_mark_write") > -1: + line = line.replace("\n", "") + trace_match = re.match(trace_regex, line) + if trace_match: + traces.append((float(trace_match.group(5)), line)) + if line.find("realtime_ts") > -1: + time_regex = trace_regex + \ + "tracing_mark_write:\s+trace_event_clock_sync: realtime_ts=(.*)" + time_match = re.match(time_regex, line) + all_real_time_dict[devices_name] = { \ + "realtime_ts": int(time_match.group(6)), \ + "timestamp": float(time_match.group(5))} + if (not all_real_time_dict.__contains__(devices_name)) and traces: + line = traces[-1][1] + trace_match = re.match(trace_regex, line) + all_real_time_dict[devices_name] = { \ + "realtime_ts": default_real_time, \ + "timestamp": float(trace_match.group(5))} + return traces + +def handle_option(): + if len(sys.argv) < 2: + print("eg: python bytrace_multi.py file1.ftrace file2.ftrace ...") + exit(0) + for i in range(len(sys.argv)): + if i == 0: + continue + if sys.argv[i] == "-h" or sys.argv[i] == "--help": + print("eg: python bytrace_multi.py file1.ftrace file2.ftrace ...") + exit(0) + elif not os.path.exists(sys.argv[i]): + print("Warning: {} is not found.".format(sys.argv[i])) + else: + files.append(sys.argv[i]) + +def change_trace_time(all_trace_list, \ + base_real_time, \ + base_time_stamp, \ + target_device): + target_real_time = all_real_time_dict[target_device]["realtime_ts"] + target_time_stamp = all_real_time_dict[target_device]["timestamp"] + if not target_real_time == default_real_time: + diff_real_time = float(target_real_time - base_real_time) / 1000 + target_time_stamp_ = base_time_stamp + diff_real_time + diff_time = target_time_stamp - target_time_stamp_ + else: + # If the file does not have realtime, the difference is 0.5s. + diff_real_time = 0.5 + target_time_stamp_ = base_time_stamp + diff_real_time + diff_time = target_time_stamp - target_time_stamp_ + traces = all_traces_dict[target_device] + for mtuple in traces: + timestamp = float(mtuple[0]) + line = mtuple[1] + timestamp_ = "{:.6f}".format(timestamp - diff_time) + line_ = target_device + \ + line.replace("{:.6f}".format(timestamp), str(timestamp_)) + all_trace_list.append((timestamp_, line_)) + +def write_to_file(data, file_name): + with codecs.open(file_name, 'w+', encoding='utf-8') as fp: + fp.write("# tracer: nop\n") + fp.write("#\n") + fp.write("# _-----=> irqs-off\n") + fp.write("# / _----=> need-resched\n") + fp.write("# | / _---=> hardirq/softirq\n") + fp.write("# || / _--=> preempt-depth\n") + fp.write("# ||| / delay\n") + fp.write("# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION\n") + fp.write("# | | | | |||| | |\n") + for mtuple in data: + fp.write(mtuple[1]) + fp.write("\n") + +def main(): + handle_option() + if len(files) == 0: + exit(-1) + + for i, val in enumerate(files): + device_name = "[device_{}]".format(str(i)) + all_traces_dict[device_name] = read_files(val, device_name) + + all_time_sorted_list = sorted(all_real_time_dict.items(), key=lambda \ + all_real_time_dict: all_real_time_dict[1]["realtime_ts"]) + base_real_time = all_time_sorted_list[0][1]["realtime_ts"] + base_time_stamp = all_time_sorted_list[0][1]["timestamp"] + all_trace_list = [] # [(timestamp, line)] + for mtuple in all_time_sorted_list: + target_device = mtuple[0] + change_trace_time(all_trace_list, \ + base_real_time, \ + base_time_stamp, \ + target_device) + # Sort by timestamp from small to large + all_trace_sorted_list = sorted(all_trace_list, key=lambda x: x[0]) + curtime = time.strftime("%Y%m%d_%H%M%S", time.localtime()) + write_to_file(all_trace_sorted_list, "multi_trace_"+str(curtime)+".ftrace") + +if __name__ == '__main__': + main()