From 5ecdd3127ed2a348b26432b3115e4b86835fa035 Mon Sep 17 00:00:00 2001 From: stesen Date: Wed, 18 Aug 2021 22:02:18 +0800 Subject: [PATCH] Description:[feature] add hitrace Signed-off-by: stesen Change-Id: I7832f0230feb5a6c4276b72dabace81bc4b31dec --- LICENSE | 177 ++++++ README.en.md | 36 -- README.md | 152 ++++- README_zh.md | 138 +++++ figures/en-us_image_0000001123644797.png | Bin 0 -> 54422 bytes figures/zh-cn_image_0000001123644797.png | Bin 0 -> 51109 bytes frameworks/native/BUILD.gn | 32 + frameworks/native/hitrace.cpp | 80 +++ frameworks/native/hitrace_inner.h | 32 + frameworks/native/hitracec.c | 365 ++++++++++++ frameworks/native/hitraceid.cpp | 105 ++++ frameworks/native/test/BUILD.gn | 71 +++ .../test/unittest/common/hitracec_test.cpp | 562 ++++++++++++++++++ .../test/unittest/common/hitracecpp_test.cpp | 561 +++++++++++++++++ interfaces/native/innerkits/BUILD.gn | 38 ++ .../innerkits/include/hitrace/hitrace.h | 49 ++ .../innerkits/include/hitrace/hitracec.h | 245 ++++++++ .../innerkits/include/hitrace/hitraceid.h | 54 ++ .../native/innerkits/include/hitrace/trace.h | 23 + lite/BUILD.gn | 43 ++ lite/test/BUILD.gn | 68 +++ ohos.build | 24 + 22 files changed, 2792 insertions(+), 63 deletions(-) create mode 100644 LICENSE delete mode 100644 README.en.md create mode 100644 README_zh.md create mode 100644 figures/en-us_image_0000001123644797.png create mode 100644 figures/zh-cn_image_0000001123644797.png create mode 100644 frameworks/native/BUILD.gn create mode 100644 frameworks/native/hitrace.cpp create mode 100644 frameworks/native/hitrace_inner.h create mode 100644 frameworks/native/hitracec.c create mode 100644 frameworks/native/hitraceid.cpp create mode 100644 frameworks/native/test/BUILD.gn create mode 100644 frameworks/native/test/unittest/common/hitracec_test.cpp create mode 100644 frameworks/native/test/unittest/common/hitracecpp_test.cpp create mode 100644 interfaces/native/innerkits/BUILD.gn create mode 100644 interfaces/native/innerkits/include/hitrace/hitrace.h create mode 100644 interfaces/native/innerkits/include/hitrace/hitracec.h create mode 100644 interfaces/native/innerkits/include/hitrace/hitraceid.h create mode 100644 interfaces/native/innerkits/include/hitrace/trace.h create mode 100644 lite/BUILD.gn create mode 100644 lite/test/BUILD.gn create mode 100644 ohos.build diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4a45986 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + 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 \ No newline at end of file diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 9841523..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# hiviewdfx_hitrace - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### 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 index 2d7a67c..e8332fd 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,137 @@ -# hiviewdfx_hitrace +# HiTrace -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +- [Overview](#section11660541593) +- [Architecture](#section16334748141112) +- [Directory Structure](#section161941989596) +- [Constraints](#section119744591305) +- [Usage](#section1312121216216) + - [Available APIs](#section1551164914237) + - [Usage Guidelines](#section129654513264) -#### 软件架构 -软件架构说明 +- [Repositories Involved](#section1371113476307) + +## Overview + +HiTrace provides APIs to implement call chain tracing throughout a service process. With HiTrace, you can quickly obtain the run log for the call chain of a specified service process and locate faults in cross-device, cross-process, or cross-thread communications. + +## Architecture + +**Figure 1** Architecture of HiTrace -#### 安装教程 +![](figures/en-us_image_0000001123644797.png) -1. xxxx -2. xxxx -3. xxxx +HiTrace is the lightweight implementation based on the distributed call chain of cloud computing. HiTrace implements call chain tracing as follows: -#### 使用说明 +- Transfers **traceid** in cross-device, cross-process, and cross-thread communications. +- Stores **traceid** in the thread local storage \(TLS\) at the Native layer of the process. +- Automatically adds **traceid** to existing events and run logs. -1. xxxx -2. xxxx -3. xxxx +## Directory Structure -#### 参与贡献 +``` +/base/hiviewdfx/hitrace +├── frameworks # Framework code +│ ├── java # HiTrace Java implementation code +│ ├── jni # HiTrace JNI implementation code +│ └── native # HiTrace Native implementation code +└── interfaces # APIs + ├── java # Java APIs + │ ├── innerkits # JAR packages opened to internal subsystems + │ └── kits # JAR packages opened to applications + └── native # C/C++ APIs + └── innerkits # Header files opened to internal subsystems +``` -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +## Constraints +HiTrace is already supported by the IPC and EventHandler communication mechanisms. If you are using a custom communication mechanism, adaptation is required to use HiTrace. -#### 特技 +## Usage + +### Available APIs + +Major APIs of HiTrace + + + + + + + + + + + + + + +

Class

+

API

+

Description

+

HiTrace

+

HiTraceId begin(String name, int flags)

+

Starts call chain tracing, generates a HiTraceId object, and sets it in the TLS of the calling thread.

+

Input parameters:

+

name: Indicates the name of the service process.

+

flags: Indicates call chain flags, which can be used in combination.

+

HITRACE_FLAG_INCLUDE_ASYNC: Traces both synchronous and asynchronous calls. By default, only synchronous calls are traced.

+

HITRACE_FLAG_DONOT_CREATE_SPAN: Do note create a span. By default, a span is created.

+

HITRACE_FLAG_TP_INFO: Outputs the tracepoint information. By default, the tracepoint information is not output.

+

HITRACE_FLAG_NO_BE_INFO: Do not output the start and end information. By default, the information is output.

+

HITRACE_FLAG_DONOT_ENABLE_LOG: Do not associate logs for output. By default, logs are associated for output.

+

HITRACE_FLAG_FAULT_TRIGGER: Triggers call chain tracing by fault. By default, call chain tracing is triggered normally.

+

HITRACE_FLAG_D2D_TP_INFO: Outputs inter-device tracepoint information. By default, the tracepoint information is not output.

+

HITRACE_FLAG_DEFAULT: Indicates the default flag.

+

Output parameters: none

+

Return value: Returns a valid HiTraceId object if call chain tracing is triggered successfully; returns an invalid object otherwise.

+

Note: In nested tracing mode, an invalid object will be returned if tracing is started at the nested layer.

+

void end(HiTraceId id)

+

Stops call chain tracing based on the HiTraceId object returned by the Begin API, and clears the HiTraceId object in the TLS of the calling thread.

+

Input parameters:

+

id: Indicates the HiTraceId object.

+

Output parameters: none

+

Return value: none

+
+ +### Usage Guidelines + +\(1\) Enable call chain tracing for the service process. + +Import the **HiTrace** and **HiTraceId** classes. + +``` +import ohos.hiviewdfx.HiTrace; +import ohos.hiviewdfx.HiTraceId; +``` + +Add the code to start or stop call chain tracing. + +``` +HiTraceId traceId = HiTrace.begin("MyServiceFlow", HiTrace.HITRACE_FLAG_DEFAULT); +... (service process for call chain tracing) +HiTrace.end(traceId); +``` + +\(2\) Run the application. + +\(3\) Use the hilog tool in the shell to filter logs of the service invoking process. + +Filter logs based on the **name** parameter in the tracing task and find **ChainId**. + +hilog | grep "MyServiceFlow" + +**Note**: The log format for traceid is as follows: + +Time PID TID Level Domain/Tag: \[**ChainId**, SpanId, ParentSpanId\] Content + +Filter the logs of the service invoking process based on **ChainId**. + +hilog | grep "ChainId" + +## Repositories Involved + +[Hivew](https://gitee.com/openharmony) + +[HiDumper](https://gitee.com/openharmony) -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 100644 index 0000000..a6bb29f --- /dev/null +++ b/README_zh.md @@ -0,0 +1,138 @@ +# HiTrace组件 + +- [简介](#section11660541593) +- [组件框架](#section16334748141112) +- [目录](#section161941989596) +- [约束](#section119744591305) +- [说明](#section1312121216216) + - [接口说明](#section1551164914237) + - [使用说明](#section129654513264) + +- [相关仓](#section1371113476307) + +## 简介 + +HiTrace在OpenHarmony中,为开发者提供业务流程调用链跟踪的维测接口。通过使用该接口所提供的功能,可以帮助开发者迅速获取指定业务流程调用链的运行日志、定位跨设备/跨进程/跨线程的故障问题。 + +## 组件框架 + +**图 1** 组件框架图 + + +![](figures/zh-cn_image_0000001123644797.png) + +HiTrace实现机制: + +- 基于云计算分布式调用链思想的轻量级实现。 +- 在跨设备/跨进程/跨线程的通信机制中传递traceid。 +- 在进程Native层TLS(Thread Local Storage)中存储traceid。 +- 在事件、运行日志中自动附加traceid。 + +## 目录 + +``` +/base/hiviewdfx/hitrace +├── frameworks # 框架代码 +│ ├── java # HiTrace java实现代码 +│ ├── jni # HiTrace jni代码 +│ └── native # HiTrace native实现代码 +└── interfaces # 接口 + ├── java # java接口 + │ ├── innerkits # 对内部子系统暴露的jar包定义 + │ └── kits # 对应用暴露的jar包定义 + └── native # C/C++接口 + └── innerkits # 对内部子系统暴露的头文件 +``` + +## 约束 + +系统通用的通信机制\(IPC, EventHandler\)已支持HiTrace机制, 对业务自定义通信机制需要适配HiTrace机制。 + +## 说明 + +### 接口说明 + +主要接口: + + + + + + + + + + + + + + +

+

方法

+

描述

+

HiTrace

+

HiTraceId begin(String name, int flags)

+

功能:启动Hitrace跟踪,生成HiTraceId对象并设置到当前线程TLS中。

+

输入参数:

+

name:业务流程名称。

+

flags:跟踪指示位,可以组合使用,具体含义为:

+

HITRACE_FLAG_INCLUDE_ASYNC:同时跟踪同步调用和异步调用,缺省只跟踪同步调用。

+

HITRACE_FLAG_DONOT_CREATE_SPAN:不创建子分支,缺省创建子分支。

+

HITRACE_FLAG_TP_INFO:输出tracepoint信息,缺省不输出。

+

HITRACE_FLAG_NO_BE_INFO:不输出起始、结束信息,缺省输出。

+

HITRACE_FLAG_DONOT_ENABLE_LOG:不与日志关联输出,缺省关联。

+

HITRACE_FLAG_FAULT_TRIGGER:故障触发的跟踪,缺省为正常启动的。

+

HITRACE_FLAG_D2D_TP_INFO:输出设备间tracepoint信息,缺省不输出。

+

HITRACE_FLAG_DEFAULT: 缺省标志。

+

输出参数:无

+

返回值:启动跟踪超过返回有效HiTraceId对象,否则返回无效对象。

+

注意:嵌套启动跟踪时,内层启动调用返回无效对象。

+

void end(HiTraceId id)

+

功能:根据begin返回的HiTraceId停止HiTrace跟踪;清除当前线程TLS中HiTraceId内容。

+

输入参数:

+

id:HiTraceId对象

+

输出参数:无

+

返回值:无

+
+ +### 使用说明 + +(1)在待跟踪的业务调用流程中插入跟踪控制代码。 + +引入类名。 + +``` +import ohos.hiviewdfx.HiTrace; +import ohos.hiviewdfx.HiTraceId; +``` + +在业务代码中使用(启动/结束跟踪): + +``` +HiTraceId traceId = HiTrace.begin("MyServiceFlow", HiTrace.HITRACE_FLAG_DEFAULT); +...... (待跟踪的业务调用流程) +HiTrace.end(traceId); +``` + +(2)运行应用程序 + +(3)shell下使用hilog工具,过滤业务调用流程的日志。 + +按启动跟踪里的name参数过滤日志,找出ChainId: + +hilog | grep “MyServiceFlow” + +注意:traceid的日志格式如下 + +Time PID TID Level Domain/Tag: \[**ChainId**, SpanId, ParentSpanId\] Content + +按ChainId过滤出业务调用流程的日志: + +hilog | grep “ChainId” + +## 相关仓 + +[HivewDfx子系统](https://gitee.com/openharmony) + +[HiDumper组件](https://gitee.com/openharmony) + diff --git a/figures/en-us_image_0000001123644797.png b/figures/en-us_image_0000001123644797.png new file mode 100644 index 0000000000000000000000000000000000000000..38f0c75ab287085c7edf4a2cd3edac4a7d79ef48 GIT binary patch literal 54422 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
    NBPzm(v{vt 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≪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 z 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 literal 0 HcmV?d00001 diff --git a/figures/zh-cn_image_0000001123644797.png b/figures/zh-cn_image_0000001123644797.png new file mode 100644 index 0000000000000000000000000000000000000000..ad51e175c1a8edcb9de14130e4bc7da15b06f0c2 GIT binary patch literal 51109 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 +#include +#include +#include +#include +#include "securec.h" +#include "hilog/log.h" +#include "hilog_trace.h" +#include "hitrace_inner.h" + +#undef LOG_DOMAIN +#undef LOG_TAG +static const unsigned int LOG_DOMAIN = 0xD002D03; +static const char* LOG_TAG = "HiTraceC"; + +typedef struct HiTraceChainIdStruct { + union { +#if __BYTE_ORDER == __LITTLE_ENDIAN + struct { + uint64_t reserved : 4; + uint64_t usecond : 20; + uint64_t second : 16; + uint64_t cpuId : 4; + uint64_t deviceId : 20; + }; + struct { + uint64_t padding : 4; + uint64_t chainId : 60; + }; +#elif __BYTE_ORDER == __BIG_ENDIAN + struct { + uint64_t deviceId : 20; + uint64_t cpuId : 4; + uint64_t second : 16; + uint64_t usecond : 20; + uint64_t reserved : 4; + }; + struct { + uint64_t chainId : 60; + uint64_t padding : 4; + }; +#else +#error "ERROR: No BIG_LITTLE_ENDIAN defines." +#endif + }; +} HiTraceChainIdStruct; + +typedef struct HiTraceIdStructExtra { + uint32_t setTls : 1; + uint32_t reserved : 31; +} HiTraceIdStructExtra; + +typedef struct HiTraceIdStructInner { + HiTraceIdStruct id; + HiTraceIdStructExtra extra; +} HiTraceIdStructInner; + +static __thread HiTraceIdStructInner g_hiTraceId = {{0, 0, 0, 0, 0, 0}, {0, 0}}; + +static inline HiTraceIdStructInner* GetThreadIdInner() +{ + return &g_hiTraceId; +} + +HiTraceIdStruct HiTraceGetId() +{ + HiTraceIdStructInner* pThreadId = GetThreadIdInner(); + return pThreadId->id; +} + +void HiTraceSetId(const HiTraceIdStruct* pId) +{ + if (!HiTraceIsValid(pId)) { + return; + } + + HiTraceIdStructInner* pThreadId = GetThreadIdInner(); + pThreadId->id = *pId; + return; +} + +void HiTraceClearId() +{ + HiTraceIdStructInner* pThreadId = GetThreadIdInner(); + HiTraceInitId(&(pThreadId->id)); + return; +} + +static inline int HiTraceGetDeviceId() +{ + // save device id and use it later + static int deviceId = 0; + + if (deviceId == 0) { + struct timeval tv; + gettimeofday(&tv, NULL); + srand(tv.tv_sec); + deviceId = random(); + } + return deviceId; +} + +static inline unsigned int HiTraceGetCpuId() +{ + // Using vdso call will make get_cpu_id faster: sched_getcpu() + static unsigned int cpuId = 0; + cpuId++; + + return cpuId; +} + +static inline uint64_t HiTraceCreateChainId() +{ + // get timestamp. Using vdso call(no system call) + struct timeval tv; + gettimeofday(&tv, NULL); + + HiTraceChainIdStruct chainId = { + .padding = 0, + .chainId = 0 + }; + chainId.deviceId = HiTraceGetDeviceId(); + chainId.cpuId = HiTraceGetCpuId(); + chainId.second = tv.tv_sec; + chainId.usecond = tv.tv_usec; + + return chainId.chainId; +} + +HiTraceIdStruct HiTraceBegin(const char* name, int flags) +{ + HiTraceIdStruct id; + HiTraceInitId(&id); + + if ((flags < HITRACE_FLAG_MIN) || (flags > HITRACE_FLAG_MAX)) { + return id; + } + + HiTraceIdStructInner* pThreadId = GetThreadIdInner(); + if (HiTraceIsValid(&(pThreadId->id))) { + return id; + } + + id.valid = HITRACE_ID_VALID; + id.ver = HITRACE_VER_1; + id.chainId = HiTraceCreateChainId(); + id.flags = flags; + id.spanId = 0; + id.parentSpanId = 0; + + pThreadId->id = id; + + if (!HiTraceIsFlagEnabled(&id, HITRACE_FLAG_NO_BE_INFO)) { + HILOG_INFO(LOG_CORE, "HiTraceBegin name:%{public}s flags:%{public}x.", name ? name : "", (int)id.flags); + } + return id; +} + +void HiTraceEnd(const HiTraceIdStruct* pId) +{ + if (!HiTraceIsValid(pId)) { + HILOG_ERROR(LOG_CORE, "HiTraceEnd error: invalid end id."); + return; + } + + HiTraceIdStructInner* pThreadId = GetThreadIdInner(); + if (!HiTraceIsValid(&(pThreadId->id))) { + HILOG_ERROR(LOG_CORE, "HiTraceEnd error: invalid thread id."); + return; + } + + if (HiTraceGetChainId(pId) != HiTraceGetChainId(&(pThreadId->id))) { + HILOG_ERROR(LOG_CORE, "HiTraceEnd error: end id(%{public}llx) != thread id(%{public}llx).", + (unsigned long long)pId->chainId, (unsigned long long)pThreadId->id.chainId); + return; + } + + if (!HiTraceIsFlagEnabled(&(pThreadId->id), HITRACE_FLAG_NO_BE_INFO)) { + HILOG_INFO(LOG_CORE, "HiTraceEnd."); + } + + HiTraceInitId(&(pThreadId->id)); + return; +} + +// BKDRHash +static uint32_t HashFunc(const void* pData, uint32_t dataLen) +{ + const uint32_t seed = 131; + + if ((!pData) || dataLen == 0) { + return 0; + } + + uint32_t hash = 0; + uint32_t len = dataLen; + char* p = (char*)pData; + + for (; len > 0; --len) { + hash = (hash * seed) + (*p++); + } + + return hash; +} + +HiTraceIdStruct HiTraceCreateSpan() +{ + static const uint32_t hashDataNum = 5; + + HiTraceIdStruct id = HiTraceGetId(); + if (!HiTraceIsValid(&id)) { + return id; + } + + if (HiTraceIsFlagEnabled(&id, HITRACE_FLAG_DONOT_CREATE_SPAN)) { + return id; + } + + // create child span id + struct timeval tv; + gettimeofday(&tv, NULL); + + uint32_t hashData[hashDataNum]; + hashData[0] = HiTraceGetDeviceId(); // 0: device id + hashData[1] = id.parentSpanId; // 1: parent span id + hashData[2] = id.spanId; // 2: span id + hashData[3] = tv.tv_sec; // 3: second + hashData[4] = tv.tv_usec; // 4: usecond + + uint32_t hash = HashFunc(hashData, hashDataNum * sizeof(uint32_t)); + + id.parentSpanId = id.spanId; + id.spanId = hash; + return id; +} + +void HiTraceTracepointInner(HiTraceCommunicationMode mode, HiTraceTracepointType type, const HiTraceIdStruct* pId, + const char* fmt, va_list args) +{ + static const int tpBufferSize = 2048; + static const char* hiTraceTypeStr[] = { "CS", "CR", "SS", "SR", "GENERAL", }; + static const char* hiTraceModeStr[] = { "DEFAULT", "THREAD", "PROCESS", "DEVICE", }; + + if (mode < HITRACE_CM_MIN || mode > HITRACE_CM_MAX) { + return; + } + if (type < HITRACE_TP_MIN || type > HITRACE_TP_MAX) { + return; + } + + if (!HiTraceIsValid(pId)) { + return; + } + + if (!HiTraceIsFlagEnabled(pId, HITRACE_FLAG_TP_INFO) && !HiTraceIsFlagEnabled(pId, HITRACE_FLAG_D2D_TP_INFO)) { + // Both tp and d2d-tp flags are disabled. + return; + } else if (!HiTraceIsFlagEnabled(pId, HITRACE_FLAG_TP_INFO) && (mode != HITRACE_CM_DEVICE)) { + // Only d2d-tp flag is enabled. But the communication mode is not device-to-device. + return; + } + + char buff[tpBufferSize]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" + // if using privacy parameter: vsnprintf => hilog_vsnprintf + int ret = vsnprintf_s(buff, tpBufferSize, tpBufferSize - 1, fmt, args); +#pragma clang diagnostic pop + if (ret == -1) { // -1: vsnprintf_s copy string fail + return; + } + buff[tpBufferSize - 1] = 0; + + HILOG_INFO(LOG_CORE, "<%{public}s,%{public}s,[%{public}llx,%{public}llx,%{public}llx]> %{public}s", + hiTraceModeStr[mode], hiTraceTypeStr[type], (unsigned long long)pId->chainId, + (unsigned long long)pId->spanId, (unsigned long long)pId->parentSpanId, buff); + return; +} + +void HiTraceTracepointWithArgs(HiTraceTracepointType type, const HiTraceIdStruct* pId, const char* fmt, va_list args) +{ + HiTraceTracepointInner(HITRACE_CM_DEFAULT, type, pId, fmt, args); +} + +void HiTraceTracepointExWithArgs(HiTraceCommunicationMode mode, HiTraceTracepointType type, const HiTraceIdStruct* pId, + const char* fmt, va_list args) +{ + HiTraceTracepointInner(mode, type, pId, fmt, args); +} + +void HiTraceTracepoint(HiTraceTracepointType type, const HiTraceIdStruct* pId, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + HiTraceTracepointInner(HITRACE_CM_DEFAULT, type, pId, fmt, args); + va_end(args); + return; +} + +void HiTraceTracepointEx(HiTraceCommunicationMode mode, HiTraceTracepointType type, const HiTraceIdStruct* pId, + const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + HiTraceTracepointInner(mode, type, pId, fmt, args); + va_end(args); + return; +} + +// return: -1 -- fail; 0 -- all valid; 1 -- all valid except span +int HiTraceGetInfo(uint64_t* pChainId, uint32_t* pFlags, uint64_t* pSpanId, uint64_t* pParentSpanId) +{ + if (!pChainId || !pFlags || !pSpanId || !pParentSpanId) { + return -1; + } + + HiTraceIdStruct id = HiTraceGetId(); + if (!HiTraceIsValid(&id)) { + return -1; + } + + if (HiTraceIsFlagEnabled(&id, HITRACE_FLAG_DONOT_ENABLE_LOG)) { + return -1; + } + + *pChainId = HiTraceGetChainId(&id); + *pFlags = HiTraceGetFlags(&id); + + if (HiTraceIsFlagEnabled(&id, HITRACE_FLAG_DONOT_CREATE_SPAN)) { + *pSpanId = 0; + *pParentSpanId = 0; + return 1; + } + + *pSpanId = HiTraceGetSpanId(&id); + *pParentSpanId = HiTraceGetParentSpanId(&id); + return 0; +} + +static void __attribute__((constructor)) HiTraceInit() +{ + // Call HiLog Register Interface + HiLogRegisterGetIdFun(HiTraceGetInfo); +} + +static void __attribute__((destructor)) HiTraceFini() +{ + HiLogUnregisterGetIdFun(HiTraceGetInfo); +} diff --git a/frameworks/native/hitraceid.cpp b/frameworks/native/hitraceid.cpp new file mode 100644 index 0000000..6aacf9c --- /dev/null +++ b/frameworks/native/hitraceid.cpp @@ -0,0 +1,105 @@ +/* + * 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 "hitrace/hitraceid.h" + +namespace OHOS { +namespace HiviewDFX { + +HiTraceId::HiTraceId() +{ + id_.valid = HITRACE_ID_INVALID; + id_.ver = 0; + id_.chainId = 0; + id_.flags = 0; + id_.spanId = 0; + id_.parentSpanId = 0; +} + +HiTraceId::HiTraceId(const HiTraceIdStruct& id) : id_(id) +{} + +HiTraceId::HiTraceId(const uint8_t* pIdArray, int len) +{ + id_ = HiTraceBytesToId(pIdArray, len); +} + +bool HiTraceId::IsValid() const +{ + return HiTraceIsValid(&id_); +} + +bool HiTraceId::IsFlagEnabled(HiTraceFlag flag) const +{ + return HiTraceIsFlagEnabled(&id_, flag); +} + +void HiTraceId::EnableFlag(HiTraceFlag flag) +{ + HiTraceEnableFlag(&id_, flag); + return; +} + +int HiTraceId::GetFlags() const +{ + return HiTraceGetFlags(&id_); +} + +void HiTraceId::SetFlags(int flags) +{ + HiTraceSetFlags(&id_, flags); + return; +} + +uint64_t HiTraceId::GetChainId() const +{ + return HiTraceGetChainId(&id_); +} + +void HiTraceId::SetChainId(uint64_t chainId) +{ + HiTraceSetChainId(&id_, chainId); + return; +} + +uint64_t HiTraceId::GetSpanId() const +{ + return HiTraceGetSpanId(&id_); +} + +void HiTraceId::SetSpanId(uint64_t spanId) +{ + HiTraceSetSpanId(&id_, spanId); + return; +} + +uint64_t HiTraceId::GetParentSpanId() const +{ + return HiTraceGetParentSpanId(&id_); +} + +void HiTraceId::SetParentSpanId(uint64_t parentSpanId) +{ + HiTraceSetParentSpanId(&id_, parentSpanId); + return; +} + +int HiTraceId::ToBytes(uint8_t* pIdArray, int len) const +{ + return HiTraceIdToBytes(&id_, pIdArray, len); +} + +} // namespace HiviewDFX +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/test/BUILD.gn b/frameworks/native/test/BUILD.gn new file mode 100644 index 0000000..6e02703 --- /dev/null +++ b/frameworks/native/test/BUILD.gn @@ -0,0 +1,71 @@ +# 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") + +module_output_path = "hiviewdfx/hitrace" + +config("module_private_config") { + visibility = [ ":*" ] + include_dirs = [ + "//base/hiviewdfx/hitrace/interfaces/native/innerkits/include", + "//base/hiviewdfx/hilog/frameworks/native/include", + "//utils/native/base/include", + ] +} + +ohos_unittest("HitraceCTest") { + module_out_path = module_output_path + + sources = [ + "../hitracec.c", + "unittest/common/hitracec_test.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] + + external_deps = [ "hilog_native:libhilog" ] +} + +ohos_unittest("HitraceCppTest") { + module_out_path = module_output_path + + sources = [ + "../hitrace.cpp", + "../hitracec.c", + "../hitraceid.cpp", + "unittest/common/hitracecpp_test.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "//third_party/googletest:gtest_main", + "//utils/native/base:utils", + ] + + external_deps = [ "hilog_native:libhilog" ] +} + +group("unittest") { + testonly = true + deps = [ + ":HitraceCTest", + ":HitraceCppTest", + ] +} diff --git a/frameworks/native/test/unittest/common/hitracec_test.cpp b/frameworks/native/test/unittest/common/hitracec_test.cpp new file mode 100644 index 0000000..1b0a5d6 --- /dev/null +++ b/frameworks/native/test/unittest/common/hitracec_test.cpp @@ -0,0 +1,562 @@ +/* + * 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 "hitrace/hitracec.h" + +#include + +namespace OHOS { +namespace HiviewDFX { + +using namespace testing::ext; + +#define HITRACE_DEBUG +#ifndef HITRACE_DEBUG +#define PRINT_ID(p) +#else +#define PRINT_ID(p) \ + printf(#p " valid:%d, ver:%d, chain:0x%llx, flags:%x, span:0x%x, pspan:0x%x.\n", static_cast((p)->valid), \ + static_cast((p)->ver), static_cast((p)->chainId), static_cast((p)->flags), \ + static_cast((p)->spanId), static_cast((p)->parentSpanId)) +#endif + +class HiTraceCTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void HiTraceCTest::SetUpTestCase() +{} + +void HiTraceCTest::TearDownTestCase() +{} + +void HiTraceCTest::SetUp() +{ + HiTraceClearId(); +} + +void HiTraceCTest::TearDown() +{} + +/** + * @tc.name: Dfx_HiTraceCTest_IdTest_001 + * @tc.desc: Get, set and clear trace id + * @tc.type: FUNC + * @tc.require: AR000CQVA0 + */ +HWTEST_F(HiTraceCTest, IdTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. get and validate trace id. + * @tc.expected: step1. trace id is invalid. + * @tc.steps: step2. construct trace id with chain id, span id, parent span id + * and flags and set it into context, then get and validate it. + * @tc.expected: step2. trace id is valid with same chain id, span id, parent + * span id and flags. + * @tc.steps: step3. construct invalid trace id and set into context, then get + * and validate it. + * @tc.expected: step3. trace id is the same with step2. + * @tc.steps: step4. clear trace id, then get and validate it. + * @tc.expected: step4. trace id is invalid. + */ + HiTraceIdStruct initId = HiTraceGetId(); + EXPECT_EQ(0, HiTraceIsValid(&initId)); + PRINT_ID(&initId); + + // set thread id + constexpr uint64_t chainId = 0xABCDEF; + constexpr uint64_t spanId = 0x12345; + constexpr uint64_t parentSpanId = 0x67890; + constexpr int flags = HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN; + HiTraceIdStruct setId; + HiTraceInitId(&setId); + HiTraceSetChainId(&setId, chainId); + HiTraceSetFlags(&setId, flags); + HiTraceSetSpanId(&setId, spanId); + HiTraceSetParentSpanId(&setId, parentSpanId); + PRINT_ID(&setId); + + HiTraceSetId(&setId); + + HiTraceIdStruct getId = HiTraceGetId(); + EXPECT_EQ(1, HiTraceIsValid(&getId)); + EXPECT_EQ(chainId, HiTraceGetChainId(&getId)); + EXPECT_EQ(HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN, HiTraceGetFlags(&getId)); + EXPECT_EQ(spanId, HiTraceGetSpanId(&getId)); + EXPECT_EQ(parentSpanId, HiTraceGetParentSpanId(&getId)); + PRINT_ID(&getId); + + // set invalid id + HiTraceIdStruct invalidId; + HiTraceInitId(&invalidId); + + HiTraceSetId(&invalidId); + + getId = HiTraceGetId(); + EXPECT_EQ(1, HiTraceIsValid(&getId)); + EXPECT_EQ(chainId, HiTraceGetChainId(&getId)); + EXPECT_EQ(HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN, HiTraceGetFlags(&getId)); + EXPECT_EQ(spanId, HiTraceGetSpanId(&getId)); + EXPECT_EQ(parentSpanId, HiTraceGetParentSpanId(&getId)); + PRINT_ID(&getId); + + // clear thread id + HiTraceClearId(); + + HiTraceIdStruct clearId = HiTraceGetId(); + EXPECT_EQ(0, HiTraceIsValid(&clearId)); + PRINT_ID(&clearId); +} + +/** + * @tc.name: Dfx_HiTraceCTest_IntfTest_001 + * @tc.desc: Interconversion between trace id and bytes array. + * @tc.type: FUNC + * @tc.require: AR000CQV9T + */ +HWTEST_F(HiTraceCTest, IntfTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. construct trace id and validate it. + * @tc.expected: step1. trace id is valid. + * @tc.steps: step2. convert trace id to bytes array. + * @tc.expected: step2. convert success when array size >= id length. + * @tc.steps: step3. convert bytes array to trace id. + * @tc.expected: step3. convert success only when array size == id length. + * @tc.steps: step4. convert invalid id to bytes array. + * @tc.expected: step4. convert fail. + * @tc.steps: step5. convert invalid bytes array to id. + * @tc.expected: step5. convert fail. + */ + // id to bytes + constexpr uint64_t chainId = 0xABCDEF; + constexpr uint64_t spanId = 0x12345; + constexpr uint64_t parentSpanId = 0x67890; + constexpr int flags = HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN; + HiTraceIdStruct id = {HITRACE_ID_VALID, HITRACE_VER_1, chainId, flags, spanId, parentSpanId}; + EXPECT_EQ(1, HiTraceIsValid(&id)); + PRINT_ID(&id); + + constexpr int idLen = sizeof(HiTraceIdStruct); + uint8_t bytes[idLen + 1]; + int len = HiTraceIdToBytes(&id, bytes, idLen - 1); + EXPECT_EQ(0, len); + len = HiTraceIdToBytes(&id, bytes, idLen + 1); + EXPECT_EQ(idLen, len); + len = HiTraceIdToBytes(&id, bytes, idLen); + EXPECT_EQ(idLen, len); + PRINT_ID(reinterpret_cast(bytes)); + + // bytes to id + HiTraceIdStruct bytesToId = HiTraceBytesToId(bytes, idLen - 1); + EXPECT_EQ(0, HiTraceIsValid(&bytesToId)); + bytesToId = HiTraceBytesToId(bytes, idLen + 1); + EXPECT_EQ(0, HiTraceIsValid(&bytesToId)); + bytesToId = HiTraceBytesToId(bytes, idLen); + EXPECT_EQ(1, HiTraceIsValid(&bytesToId)); + EXPECT_EQ(chainId, HiTraceGetChainId(&bytesToId)); + EXPECT_EQ(HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN, HiTraceGetFlags(&bytesToId)); + EXPECT_EQ(spanId, HiTraceGetSpanId(&bytesToId)); + EXPECT_EQ(parentSpanId, HiTraceGetParentSpanId(&bytesToId)); + PRINT_ID(&bytesToId); + + // set invalid id + HiTraceIdStruct invalidId; + HiTraceInitId(&invalidId); + EXPECT_EQ(0, HiTraceIdToBytes(&invalidId, bytes, idLen)); + invalidId = HiTraceBytesToId(nullptr, idLen); + EXPECT_EQ(0, HiTraceIsValid(&invalidId)); +} + +/** + * @tc.name: Dfx_HiTraceCTest_IntfTest_002 + * @tc.desc: Start and stop trace. + * @tc.type: FUNC + * @tc.require: AR000CQV9T + */ +HWTEST_F(HiTraceCTest, IntfTest_002, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with flags, get trace id and validit it. + * @tc.expected: step1. trace id and flags is valid. + * @tc.steps: step2. stop trace, get trace id and validit it. + * @tc.expected: step2. trace id is invalid. + */ + // begin + HiTraceIdStruct beginId = HiTraceBegin("test", HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_NO_BE_INFO); + EXPECT_EQ(1, HiTraceIsValid(&beginId)); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&beginId, HITRACE_FLAG_INCLUDE_ASYNC)); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&beginId, HITRACE_FLAG_NO_BE_INFO)); + PRINT_ID(&beginId); + + // end + HiTraceEnd(&beginId); + + HiTraceIdStruct endId = HiTraceGetId(); + EXPECT_EQ(0, HiTraceIsValid(&endId)); + PRINT_ID(&endId); +} + +/** + * @tc.name: Dfx_HiTraceCTest_IntfTest_003 + * @tc.desc: Start and stop trace with reentered. + * @tc.type: FUNC + * @tc.require: AR000CQV9T + */ +HWTEST_F(HiTraceCTest, IntfTest_003, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace twice and get 2nd trace id. + * @tc.expected: step1. 2nd trace is invalid. + * @tc.steps: step2. get trace id and check. + * @tc.expected: step2. trace id is valid and same with 1st id. + * @tc.steps: step3. set chain id with wrong id and get trace id. + * @tc.expected: step3. trace id is valid and same with 1st id. + * @tc.steps: step4. stop trace twice and get trace id. + * @tc.expected: step4. trace id is invalid. + */ + HiTraceIdStruct beginId = HiTraceBegin("begin", HITRACE_FLAG_INCLUDE_ASYNC); + PRINT_ID(&beginId); + + // reenter begin + HiTraceIdStruct reBeginId = HiTraceBegin("reenter begin", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(0, HiTraceIsValid(&reBeginId)); + EXPECT_NE(HiTraceGetChainId(&reBeginId), HiTraceGetChainId(&beginId)); + EXPECT_EQ(0, HiTraceIsFlagEnabled(&reBeginId, HITRACE_FLAG_INCLUDE_ASYNC)); + EXPECT_EQ(0, HiTraceIsFlagEnabled(&reBeginId, HITRACE_FLAG_TP_INFO)); + PRINT_ID(&reBeginId); + + // reenter end + HiTraceEnd(&reBeginId); + + HiTraceIdStruct endId = HiTraceGetId(); + EXPECT_EQ(1, HiTraceIsValid(&endId)); + EXPECT_EQ(HiTraceGetChainId(&endId), HiTraceGetChainId(&beginId)); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&endId, HITRACE_FLAG_INCLUDE_ASYNC)); + EXPECT_EQ(0, HiTraceIsFlagEnabled(&endId, HITRACE_FLAG_TP_INFO)); + PRINT_ID(&endId); + + // end with wrong chainId + HiTraceIdStruct wrongBeginId = beginId; + HiTraceSetChainId(&wrongBeginId, HiTraceGetChainId(&beginId) + 1); + HiTraceEnd(&wrongBeginId); + + HiTraceIdStruct wrongEndId = HiTraceGetId(); + EXPECT_EQ(1, HiTraceIsValid(&wrongEndId)); + EXPECT_EQ(HiTraceGetChainId(&wrongEndId), HiTraceGetChainId(&beginId)); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&wrongEndId, HITRACE_FLAG_INCLUDE_ASYNC)); + PRINT_ID(&wrongEndId); + + // end + HiTraceEnd(&beginId); + + HiTraceIdStruct reEndId = HiTraceGetId(); + EXPECT_EQ(0, HiTraceIsValid(&reEndId)); + PRINT_ID(&reEndId); + + // end with invalid thread id + HiTraceEnd(&beginId); + + HiTraceIdStruct endInvalidId = HiTraceGetId(); + EXPECT_EQ(0, HiTraceIsValid(&endInvalidId)); + PRINT_ID(&endInvalidId); +} + +/** + * @tc.name: Dfx_HiTraceCTest_SpanTest_001 + * @tc.desc: Create child and grand child span. + * @tc.type: FUNC + * @tc.require: AR000CQVA2 + */ +HWTEST_F(HiTraceCTest, SpanTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace without HITRACE_FLAG_DONOT_CREATE_SPAN, + * get and check flags. + * @tc.expected: step1. flags is same with set and span id is 0. + * @tc.steps: step2. create child id. + * @tc.expected: step2. child id has same span id with parent. + * @tc.steps: step3. set child id into context. + * @tc.steps: step4. create grand child id. + * @tc.expected: step4. grand child id has same span id with parent and child. + */ + HiTraceIdStruct id = HiTraceBegin("test", 0); + EXPECT_EQ(0, HiTraceGetFlags(&id)); + EXPECT_EQ(0UL, HiTraceGetSpanId(&id)); + EXPECT_EQ(0UL, HiTraceGetParentSpanId(&id)); + PRINT_ID(&id); + + // create child span + HiTraceIdStruct childId = HiTraceCreateSpan(); + EXPECT_EQ(1, HiTraceIsValid(&childId)); + EXPECT_EQ(HiTraceGetFlags(&childId), HiTraceGetFlags(&id)); + EXPECT_EQ(HiTraceGetChainId(&childId), HiTraceGetChainId(&id)); + EXPECT_EQ(HiTraceGetParentSpanId(&childId), HiTraceGetSpanId(&id)); + PRINT_ID(&childId); + + // set child id to thread id + HiTraceSetId(&childId); + + // continue to create child span + HiTraceIdStruct grandChildId = HiTraceCreateSpan(); + EXPECT_EQ(1, HiTraceIsValid(&grandChildId)); + EXPECT_EQ(HiTraceGetFlags(&grandChildId), HiTraceGetFlags(&id)); + EXPECT_EQ(HiTraceGetChainId(&grandChildId), HiTraceGetChainId(&id)); + EXPECT_EQ(HiTraceGetParentSpanId(&grandChildId), HiTraceGetSpanId(&childId)); + PRINT_ID(&grandChildId); + + // end + HiTraceEnd(&id); +} + +/** + * @tc.name: Dfx_HiTraceCTest_SpanTest_002 + * @tc.desc: Start and stop trace with reentered. + * @tc.type: FUNC + * @tc.require: AR000CQVA2 + */ +HWTEST_F(HiTraceCTest, SpanTest_002, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_DONOT_CREATE_SPAN, + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_DONOT_CREATE_SPAN is enabled. + * @tc.steps: step2. create child id. + * @tc.expected: step2. child id is same with parent id. + */ + // begin with "donot create span" flag + HiTraceIdStruct id = HiTraceBegin("test", HITRACE_FLAG_DONOT_CREATE_SPAN); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_DONOT_CREATE_SPAN)); + PRINT_ID(&id); + + // create child span + HiTraceIdStruct childId = HiTraceCreateSpan(); + EXPECT_EQ(1, HiTraceIsValid(&childId)); + EXPECT_EQ(HiTraceGetFlags(&childId), HiTraceGetFlags(&id)); + EXPECT_EQ(HiTraceGetChainId(&childId), HiTraceGetChainId(&id)); + EXPECT_EQ(HiTraceGetSpanId(&childId), HiTraceGetSpanId(&id)); + EXPECT_EQ(HiTraceGetParentSpanId(&childId), HiTraceGetParentSpanId(&id)); + PRINT_ID(&childId); + + // end + HiTraceEnd(&id); +} + +/** + * @tc.name: Dfx_HiTraceCTest_TracepointTest_001 + * @tc.desc: Start trace with HITRACE_FLAG_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCTest, TracepointTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_TP_INFO, + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_TP_INFO is enabled. + * @tc.steps: step2. add trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + * @tc.steps: step2. add trace point info with null and check logs. + * @tc.expected: step2. trace point cannot be found in logs. + */ + HiTraceIdStruct id = HiTraceBegin("test tp flag", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_TP_INFO)); + HiTraceTracepoint(HITRACE_TP_CS, &id, "client send msg content %d", 12); + + HiTraceTracepoint(HITRACE_TP_CS, nullptr, "client send msg content %d", 12); + + HiTraceEnd(&id); +} + +/** + * @tc.name: Dfx_HiTraceCTest_TracepointTest_002 + * @tc.desc: Start trace without HITRACE_FLAG_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCTest, TracepointTest_002, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace without HITRACE_FLAG_TP_INFO flag. + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_TP_INFO is not enabled. + * @tc.steps: step2. add trace point info with id and check logs. + * @tc.expected: step2. trace point cannot be found in logs. + */ + // begin with tp flag + HiTraceIdStruct id = HiTraceBegin("test no tp flag", HITRACE_FLAG_INCLUDE_ASYNC); + EXPECT_EQ(0, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_TP_INFO)); + HiTraceTracepoint(HITRACE_TP_CS, &id, "client send msg content %d", 12); + + HiTraceEnd(&id); +} + +/** + * @tc.name: Dfx_HiTraceCTest_TracepointTest_003 + * @tc.desc: Start trace with HITRACE_FLAG_D2D_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCTest, TracepointTest_003, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_D2D_TP_INFO, + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_D2D_TP_INFO is enabled. + * @tc.steps: step2. add D2D trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + * @tc.steps: step2. add D2D trace point info with null and check logs. + * @tc.expected: step2. trace point cannot be found in logs. + * @tc.steps: step3. add trace point info with id and check logs. + * @tc.expected: step3. trace point cannot be found in logs. + */ + HiTraceIdStruct id = HiTraceBegin("test D2D tp flag", HITRACE_FLAG_D2D_TP_INFO); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_D2D_TP_INFO)); + HiTraceTracepointEx(HITRACE_CM_DEVICE, HITRACE_TP_CS, &id, "client send msg content %d", 12); + HiTraceTracepointEx(HITRACE_CM_PROCESS, HITRACE_TP_CS, &id, "cannot be found %d", 22); + HiTraceTracepointEx(HITRACE_CM_THREAD, HITRACE_TP_CS, &id, "cannot be found %d", 32); + HiTraceTracepointEx(HITRACE_CM_DEFAULT, HITRACE_TP_CS, &id, "cannot be found %d", 42); + + HiTraceTracepointEx(HITRACE_CM_DEVICE, HITRACE_TP_CS, nullptr, "cannot be found %d", 13); + + HiTraceTracepoint(HITRACE_TP_CS, &id, "cannot be found %d", 14); + + HiTraceEnd(&id); +} + +/** + * @tc.name: Dfx_HiTraceCTest_TracepointTest_004 + * @tc.desc: Start trace without HITRACE_FLAG_D2D_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCTest, TracepointTest_004, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace without HITRACE_FLAG_D2D_TP_INFO flag. + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_D2D_TP_INFO is not enabled. + * @tc.steps: step2. add D2D trace point info with id and check logs. + * @tc.expected: step2. trace point cannot be found in logs. + */ + HiTraceIdStruct id = HiTraceBegin("test no D2D tp flag", HITRACE_FLAG_INCLUDE_ASYNC); + EXPECT_EQ(0, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_D2D_TP_INFO)); + HiTraceTracepointEx(HITRACE_CM_DEVICE, HITRACE_TP_CS, &id, "cannot be found %d", 12); + HiTraceTracepointEx(HITRACE_CM_PROCESS, HITRACE_TP_CS, &id, "cannot be found %d", 22); + HiTraceTracepointEx(HITRACE_CM_THREAD, HITRACE_TP_CS, &id, "cannot be found %d", 32); + HiTraceTracepointEx(HITRACE_CM_DEFAULT, HITRACE_TP_CS, &id, "cannot be found %d", 42); + + HiTraceEnd(&id); +} + +/** + * @tc.name: Dfx_HiTraceCTest_TracepointTest_005 + * @tc.desc: Start trace with HITRACE_FLAG_D2D_TP_INFO and HITRACE_FLAG_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCTest, TracepointTest_005, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_D2D_TP_INFO | HITRACE_FLAG_TP_INFO, + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_D2D_TP_INFO is enabled. + * @tc.expected: step1. HITRACE_FLAG_TP_INFO is enabled. + * @tc.steps: step2. add D2D trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + * @tc.steps: step3. add trace point info with id and check logs. + * @tc.expected: step3. trace point can be found in logs. + */ + HiTraceIdStruct id = HiTraceBegin("test D2D | TP tp flag", HITRACE_FLAG_D2D_TP_INFO | HITRACE_FLAG_TP_INFO); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_D2D_TP_INFO)); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_TP_INFO)); + HiTraceTracepointEx(HITRACE_CM_DEVICE, HITRACE_TP_CS, &id, "client send msg content %d", 12); + HiTraceTracepointEx(HITRACE_CM_PROCESS, HITRACE_TP_CS, &id, "client send msg content %d", 22); + HiTraceTracepointEx(HITRACE_CM_THREAD, HITRACE_TP_CS, &id, "client send msg content %d", 32); + HiTraceTracepointEx(HITRACE_CM_DEFAULT, HITRACE_TP_CS, &id, "client send msg content %d", 42); + + HiTraceTracepoint(HITRACE_TP_CS, &id, "client send msg content %d", 13); + + HiTraceEnd(&id); +} + +/** + * @tc.name: Dfx_HiTraceCTest_TracepointTest_006 + * @tc.desc: Start trace without HITRACE_FLAG_D2D_TP_INFO, but with HITRACE_FLAG_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCTest, TracepointTest_006, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_TP_INFO flag. + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_D2D_TP_INFO is not enabled. + * * @tc.expected: step1. HITRACE_FLAG_TP_INFO is enabled. + * @tc.steps: step2. add D2D trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + * @tc.steps: step2. add trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + */ + HiTraceIdStruct id = HiTraceBegin("test no D2D, but tp flag", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(0, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_D2D_TP_INFO)); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&id, HITRACE_FLAG_TP_INFO)); + HiTraceTracepointEx(HITRACE_CM_DEVICE, HITRACE_TP_CS, &id, "client send msg content %d", 12); + HiTraceTracepointEx(HITRACE_CM_PROCESS, HITRACE_TP_CS, &id, "client send msg content %d", 22); + HiTraceTracepointEx(HITRACE_CM_THREAD, HITRACE_TP_CS, &id, "client send msg content %d", 32); + HiTraceTracepointEx(HITRACE_CM_DEFAULT, HITRACE_TP_CS, &id, "client send msg content %d", 42); + + HiTraceTracepoint(HITRACE_TP_CS, &id, "client send msg content %d", 13); + + HiTraceEnd(&id); +} + +/** + * @tc.name: Dfx_HiTraceCTest_SyncAsyncTest_001 + * @tc.desc: Start trace with SYNC or ASYNC. + * @tc.type: FUNC + * @tc.require: AR000CQ0G7 + */ +HWTEST_F(HiTraceCTest, SyncAsyncTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace without HITRACE_FLAG_INCLUDE_ASYNC flag. + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_INCLUDE_ASYNC is not enabled. + * @tc.steps: step2. start trace with HITRACE_FLAG_INCLUDE_ASYNC flag. + * get and check flags. + * @tc.expected: step2. HITRACE_FLAG_INCLUDE_ASYNC is enabled. + */ + // begin with sync flag + HiTraceIdStruct syncId = HiTraceBegin("test sync only", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(0, HiTraceIsFlagEnabled(&syncId, HITRACE_FLAG_INCLUDE_ASYNC)); + HiTraceTracepoint(HITRACE_TP_CS, &syncId, "client send msg: %s", "sync"); + + HiTraceEnd(&syncId); + + // begin with async flag + HiTraceIdStruct asyncId = HiTraceBegin("test sync+async", HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_TP_INFO); + EXPECT_EQ(1, HiTraceIsFlagEnabled(&asyncId, HITRACE_FLAG_INCLUDE_ASYNC)); + HiTraceTracepoint(HITRACE_TP_CS, &asyncId, "client send msg: %s", "async"); + + HiTraceEnd(&asyncId); +} + +} // namespace HiviewDFX +} // namespace OHOS diff --git a/frameworks/native/test/unittest/common/hitracecpp_test.cpp b/frameworks/native/test/unittest/common/hitracecpp_test.cpp new file mode 100644 index 0000000..eb326b0 --- /dev/null +++ b/frameworks/native/test/unittest/common/hitracecpp_test.cpp @@ -0,0 +1,561 @@ +/* + * 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 "hitrace/hitrace.h" + +#include + +namespace OHOS { +namespace HiviewDFX { + +using namespace testing::ext; + +class HiTraceCppTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void HiTraceCppTest::SetUpTestCase() +{} + +void HiTraceCppTest::TearDownTestCase() +{} + +void HiTraceCppTest::SetUp() +{ + HiTrace::ClearId(); +} + +void HiTraceCppTest::TearDown() +{} + +/** + * @tc.name: Dfx_HiTraceCppTest_IdTest_001 + * @tc.desc: Get, set and clear trace id + * @tc.type: FUNC + * @tc.require: AR000CQV9U + */ +HWTEST_F(HiTraceCppTest, IdTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. get and validate trace id. + * @tc.expected: step1. trace id is invalid. + * @tc.steps: step2. construct trace id with chain id, span id, parent span id + * and flags and set it into context, then get and validate it. + * @tc.expected: step2. trace id is valid with same chain id, span id, parent + * span id and flags. + * @tc.steps: step3. construct invalid trace id and set into context, then get + * and validate it. + * @tc.expected: step3. trace id is the same with step2. + * @tc.steps: step4. clear trace id, then get and validate it. + * @tc.expected: step4. trace id is invalid. + */ + HiTraceId initId = HiTrace::GetId(); + EXPECT_EQ(0, initId.IsValid()); + /* set thread id */ + constexpr uint64_t CHAIN_ID = 0xABCDEF; + constexpr uint64_t SPAN_ID = 0x12345; + constexpr uint64_t PARENT_SPAN_ID = 0x67890; + + initId.SetChainId(CHAIN_ID); + initId.EnableFlag(HITRACE_FLAG_INCLUDE_ASYNC); + initId.EnableFlag(HITRACE_FLAG_DONOT_CREATE_SPAN); + initId.SetSpanId(SPAN_ID); + initId.SetParentSpanId(PARENT_SPAN_ID); + + EXPECT_EQ(1, initId.IsValid()); + EXPECT_EQ(CHAIN_ID, initId.GetChainId()); + EXPECT_EQ(HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN, initId.GetFlags()); + EXPECT_EQ(SPAN_ID, initId.GetSpanId()); + EXPECT_EQ(PARENT_SPAN_ID, initId.GetParentSpanId()); + + HiTrace::SetId(initId); + + HiTraceId getId = HiTrace::GetId(); + EXPECT_EQ(1, getId.IsValid()); + EXPECT_EQ(CHAIN_ID, getId.GetChainId()); + EXPECT_EQ(HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN, getId.GetFlags()); + EXPECT_EQ(SPAN_ID, getId.GetSpanId()); + EXPECT_EQ(PARENT_SPAN_ID, getId.GetParentSpanId()); + + HiTraceId invalidId; + HiTrace::SetId(invalidId); + + getId = HiTrace::GetId(); + EXPECT_EQ(1, getId.IsValid()); + EXPECT_EQ(CHAIN_ID, getId.GetChainId()); + EXPECT_EQ(HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN, getId.GetFlags()); + EXPECT_EQ(SPAN_ID, getId.GetSpanId()); + EXPECT_EQ(PARENT_SPAN_ID, getId.GetParentSpanId()); + + HiTrace::ClearId(); + HiTraceId clearId = HiTrace::GetId(); + EXPECT_EQ(0, clearId.IsValid()); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_IntfTest_001 + * @tc.desc: Interconversion between trace id and bytes array. + * @tc.type: FUNC + * @tc.require: AR000CQV9U + */ +HWTEST_F(HiTraceCppTest, IntfTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. get trace id and validate it. + * @tc.expected: step1. trace id is invalid. + * @tc.steps: step2. construct trace id and validate it. + * @tc.expected: step2. trace id is valid. + * @tc.steps: step3. convert trace id to bytes array. + * @tc.expected: step3. convert success when array size >= id length. + * @tc.steps: step4. convert bytes array to trace id. + * @tc.expected: step4. convert success only when array size == id length. + * @tc.steps: step5. convert invalid id to bytes array. + * @tc.expected: step5. convert fail. + * @tc.steps: step6. convert invalid bytes array to id. + * @tc.expected: step6. convert fail. + */ + HiTraceId initId = HiTrace::GetId(); + EXPECT_EQ(0, initId.IsValid()); + constexpr uint64_t CHAIN_ID = 0xABCDEF; + constexpr uint64_t SPAN_ID = 0x12345; + constexpr uint64_t PARENT_SPAN_ID = 0x67890; + constexpr int FLAGS = HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN; + + initId.SetChainId(CHAIN_ID); + initId.SetFlags(FLAGS); + initId.SetSpanId(SPAN_ID); + initId.SetParentSpanId(PARENT_SPAN_ID); + EXPECT_EQ(1, initId.IsValid()); + constexpr int ID_LEN = sizeof(HiTraceIdStruct); + + uint8_t bytes[ID_LEN + 1]; + int len = initId.ToBytes(bytes, ID_LEN - 1); + EXPECT_EQ(0, len); + len = initId.ToBytes(bytes, ID_LEN + 1); + EXPECT_EQ(ID_LEN, len); + len = initId.ToBytes(bytes, ID_LEN); + EXPECT_EQ(ID_LEN, len); + + /* bytes to id */ + HiTraceId bytesToId = HiTraceId(bytes, ID_LEN - 1); + EXPECT_EQ(0, bytesToId.IsValid()); + bytesToId = HiTraceId(bytes, ID_LEN + 1); + EXPECT_EQ(0, bytesToId.IsValid()); + bytesToId = HiTraceId(bytes, ID_LEN); + EXPECT_EQ(1, bytesToId.IsValid()); + EXPECT_EQ(CHAIN_ID, bytesToId.GetChainId()); + EXPECT_EQ(HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_DONOT_CREATE_SPAN, bytesToId.GetFlags()); + EXPECT_EQ(SPAN_ID, bytesToId.GetSpanId()); + EXPECT_EQ(PARENT_SPAN_ID, bytesToId.GetParentSpanId()); + + /* set invalid id */ + HiTraceId invalidId; + EXPECT_EQ(0, invalidId.ToBytes(bytes, ID_LEN)); + invalidId = HiTraceId(nullptr, ID_LEN); + EXPECT_EQ(0, invalidId.IsValid()); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_IntfTest_002 + * @tc.desc: Start and stop trace. + * @tc.type: FUNC + * @tc.require: AR000CQV9U + */ +HWTEST_F(HiTraceCppTest, IntfTest_002, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with flags, get trace id and validit it. + * @tc.expected: step1. trace id and flags is valid. + * @tc.steps: step2. stop trace, get trace id and validit it. + * @tc.expected: step2. trace id is invalid. + */ + HiTraceId beginId = HiTrace::Begin("test", HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_NO_BE_INFO); + EXPECT_EQ(1, beginId.IsValid()); + EXPECT_EQ(1, beginId.IsFlagEnabled(HITRACE_FLAG_INCLUDE_ASYNC)); + EXPECT_EQ(1, beginId.IsFlagEnabled(HITRACE_FLAG_NO_BE_INFO)); + + HiTrace::End(beginId); + + HiTraceId endId = HiTrace::GetId(); + EXPECT_EQ(0, endId.IsValid()); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_IntfTest_003 + * @tc.desc: Start and stop trace with reentered. + * @tc.type: FUNC + * @tc.require: AR000CQV9U + */ +HWTEST_F(HiTraceCppTest, IntfTest_003, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace twice and get 2nd trace id. + * @tc.expected: step1. 2nd trace is invalid. + * @tc.steps: step2. get trace id and check. + * @tc.expected: step2. trace id is valid and same with 1st id. + * @tc.steps: step3. set chain id with wrong id and get trace id. + * @tc.expected: step3. trace id is valid and same with 1st id. + * @tc.steps: step4. stop trace twice and get trace id. + * @tc.expected: step4. trace id is invalid. + */ + /* begin */ + HiTraceId beginId = HiTrace::Begin("begin", HITRACE_FLAG_INCLUDE_ASYNC); + /* reenter begin */ + HiTraceId reBeginId = HiTrace::Begin("reenter begin", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(0, reBeginId.IsValid()); + EXPECT_NE(reBeginId.GetChainId(), beginId.GetChainId()); + EXPECT_EQ(0, reBeginId.IsFlagEnabled(HITRACE_FLAG_INCLUDE_ASYNC)); + EXPECT_EQ(0, reBeginId.IsFlagEnabled(HITRACE_FLAG_TP_INFO)); + + /* reenter end */ + HiTrace::End(reBeginId); + + HiTraceId endId = HiTrace::GetId(); + EXPECT_EQ(1, endId.IsValid()); + EXPECT_EQ(endId.GetChainId(), beginId.GetChainId()); + EXPECT_EQ(1, endId.IsFlagEnabled(HITRACE_FLAG_INCLUDE_ASYNC)); + EXPECT_EQ(0, endId.IsFlagEnabled(HITRACE_FLAG_TP_INFO)); + + /* end with wrong chainId */ + HiTraceId wrongBeginId = beginId; + wrongBeginId.SetChainId(beginId.GetChainId() + 1); + HiTrace::End(wrongBeginId); + + HiTraceId wrongEndId = HiTrace::GetId(); + EXPECT_EQ(1, wrongEndId.IsValid()); + EXPECT_EQ(wrongEndId.GetChainId(), beginId.GetChainId()); + EXPECT_EQ(1, wrongEndId.IsFlagEnabled(HITRACE_FLAG_INCLUDE_ASYNC)); + + /* end */ + HiTrace::End(beginId); + + HiTraceId reEndId = HiTrace::GetId(); + EXPECT_EQ(0, reEndId.IsValid()); + + /* end with invalid thread id */ + HiTrace::End(beginId); + + HiTraceId endInvalidId = HiTrace::GetId(); + EXPECT_EQ(0, endInvalidId.IsValid()); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_SpanTest_001 + * @tc.desc: Create child and grand child span. + * @tc.type: FUNC + * @tc.require: AR000CQV9U + */ +HWTEST_F(HiTraceCppTest, SpanTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace without HITRACE_FLAG_DONOT_CREATE_SPAN, + * get and check flags. + * @tc.expected: step1. flags is same with set and span id is 0. + * @tc.steps: step2. create child id. + * @tc.expected: step2. child id has same span id with parent. + * @tc.steps: step3. set child id into context. + * @tc.steps: step4. create grand child id. + * @tc.expected: step4. grand child id has same span id with parent and child. + */ + /* begin with span flag */ + HiTraceId id = HiTrace::Begin("test", 0); + EXPECT_EQ(0, id.GetFlags()); + EXPECT_EQ(0UL, id.GetSpanId()); + EXPECT_EQ(0UL, id.GetParentSpanId()); + + /* create child span */ + HiTraceId childId = HiTrace::CreateSpan(); + EXPECT_EQ(1, childId.IsValid()); + EXPECT_EQ(childId.GetFlags(), id.GetFlags()); + EXPECT_EQ(childId.GetChainId(), id.GetChainId()); + EXPECT_EQ(childId.GetParentSpanId(), id.GetSpanId()); + + /* set child id to thread id */ + HiTrace::SetId(childId); + + /* continue to create child span */ + HiTraceId grandChildId = HiTrace::CreateSpan(); + EXPECT_EQ(1, grandChildId.IsValid()); + EXPECT_EQ(grandChildId.GetFlags(), id.GetFlags()); + EXPECT_EQ(grandChildId.GetChainId(), id.GetChainId()); + EXPECT_EQ(grandChildId.GetParentSpanId(), childId.GetSpanId()); + + /* end */ + HiTrace::End(id); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_SpanTest_002 + * @tc.desc: Start and stop trace with reentered. + * @tc.type: FUNC + * @tc.require: AR000CQV9U + */ +HWTEST_F(HiTraceCppTest, SpanTest_002, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_DONOT_CREATE_SPAN, + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_DONOT_CREATE_SPAN is enabled. + * @tc.steps: step2. create child id. + * @tc.expected: step2. child id is same with parent id. + */ + /* begin with "donot create span" flag */ + HiTraceId id = HiTrace::Begin("test", HITRACE_FLAG_DONOT_CREATE_SPAN); + EXPECT_EQ(1, id.IsFlagEnabled(HITRACE_FLAG_DONOT_CREATE_SPAN)); + + /* create child span */ + HiTraceId childId = HiTrace::CreateSpan(); + EXPECT_EQ(1, childId.IsValid()); + EXPECT_EQ(childId.GetFlags(), id.GetFlags()); + EXPECT_EQ(childId.GetChainId(), id.GetChainId()); + EXPECT_EQ(childId.GetSpanId(), id.GetSpanId()); + EXPECT_EQ(childId.GetParentSpanId(), id.GetParentSpanId()); + + /* end */ + HiTrace::End(id); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_TracepointTest_001 + * @tc.desc: Start trace with HITRACE_FLAG_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCppTest, TracepointTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_TP_INFO, + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_TP_INFO is enabled. + * @tc.steps: step2. add trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + * @tc.steps: step3. add trace point info with null and check logs. + * @tc.expected: step3. trace point cannot be found in logs. + */ + /* begin with tp flag */ + HiTraceId invalidId; + HiTraceId id = HiTrace::Begin("test tp flag", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(1, id.IsFlagEnabled(HITRACE_FLAG_TP_INFO)); + HiTrace::Tracepoint(HITRACE_TP_CS, id, "client send msg content %d", 12); + HiTrace::Tracepoint(HITRACE_TP_CS, invalidId, "client send msg content %d", 12); + HiTrace::End(id); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_TracepointTest_002 + * @tc.desc: Start trace without HITRACE_FLAG_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCppTest, TracepointTest_002, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace without HITRACE_FLAG_TP_INFO flag. + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_TP_INFO is not enabled. + * @tc.steps: step2. add trace point info with id and check logs. + * @tc.expected: step2. trace point cannot be found in logs. + */ + /* begin with tp flag */ + HiTraceId id = HiTrace::Begin("test no tp flag", HITRACE_FLAG_INCLUDE_ASYNC); + EXPECT_EQ(0, id.IsFlagEnabled(HITRACE_FLAG_TP_INFO)); + HiTrace::Tracepoint(HITRACE_TP_CS, id, "client send msg content %d", 12); + + HiTrace::End(id); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_TracepointTest_003 + * @tc.desc: Start trace with HITRACE_FLAG_D2D_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCppTest, TracepointTest_003, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_D2D_TP_INFO, + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_D2D_TP_INFO is enabled. + * @tc.steps: step2. add D2D trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + * @tc.steps: step2. add D2D trace point info with null and check logs. + * @tc.expected: step2. trace point cannot be found in logs. + * @tc.steps: step3. add trace point info with id and check logs. + * @tc.expected: step3. trace point cannot be found in logs. + */ + HiTraceId id = HiTrace::Begin("test D2D tp flag", HITRACE_FLAG_D2D_TP_INFO); + EXPECT_EQ(1, id.IsFlagEnabled(HITRACE_FLAG_D2D_TP_INFO)); + HiTrace::Tracepoint(HITRACE_CM_DEVICE, HITRACE_TP_CS, id, "client send msg content %d", 12); + HiTrace::Tracepoint(HITRACE_CM_PROCESS, HITRACE_TP_CS, id, "cannot be found %d", 22); + HiTrace::Tracepoint(HITRACE_CM_THREAD, HITRACE_TP_CS, id, "cannot be found %d", 32); + HiTrace::Tracepoint(HITRACE_CM_DEFAULT, HITRACE_TP_CS, id, "cannot be found %d", 42); + + HiTraceId invalidId; + HiTrace::Tracepoint(HITRACE_CM_DEVICE, HITRACE_TP_CS, invalidId, "cannot be found %d", 13); + + HiTrace::Tracepoint(HITRACE_TP_CS, id, "cannot be found %d", 14); + + HiTrace::End(id); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_TracepointTest_004 + * @tc.desc: Start trace without HITRACE_FLAG_D2D_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCppTest, TracepointTest_004, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace without HITRACE_FLAG_D2D_TP_INFO flag. + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_D2D_TP_INFO is not enabled. + * @tc.steps: step2. add D2D trace point info with id and check logs. + * @tc.expected: step2. trace point cannot be found in logs. + */ + HiTraceId id = HiTrace::Begin("test no D2D tp flag", HITRACE_FLAG_INCLUDE_ASYNC); + EXPECT_EQ(0, id.IsFlagEnabled(HITRACE_FLAG_D2D_TP_INFO)); + HiTrace::Tracepoint(HITRACE_CM_DEVICE, HITRACE_TP_CS, id, "cannot be found %d", 12); + HiTrace::Tracepoint(HITRACE_CM_PROCESS, HITRACE_TP_CS, id, "cannot be found %d", 22); + HiTrace::Tracepoint(HITRACE_CM_THREAD, HITRACE_TP_CS, id, "cannot be found %d", 32); + HiTrace::Tracepoint(HITRACE_CM_DEFAULT, HITRACE_TP_CS, id, "cannot be found %d", 42); + + HiTrace::End(id); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_TracepointTest_005 + * @tc.desc: Start trace with HITRACE_FLAG_D2D_TP_INFO and HITRACE_FLAG_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCppTest, TracepointTest_005, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_D2D_TP_INFO | HITRACE_FLAG_TP_INFO, + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_D2D_TP_INFO is enabled. + * @tc.expected: step1. HITRACE_FLAG_TP_INFO is enabled. + * @tc.steps: step2. add D2D trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + * @tc.steps: step3. add trace point info with id and check logs. + * @tc.expected: step3. trace point can be found in logs. + */ + HiTraceId id = HiTrace::Begin("test D2D | TP tp flag", HITRACE_FLAG_D2D_TP_INFO | HITRACE_FLAG_TP_INFO); + EXPECT_EQ(1, id.IsFlagEnabled(HITRACE_FLAG_D2D_TP_INFO)); + EXPECT_EQ(1, id.IsFlagEnabled(HITRACE_FLAG_TP_INFO)); + HiTrace::Tracepoint(HITRACE_CM_DEVICE, HITRACE_TP_CS, id, "client send msg content %d", 12); + HiTrace::Tracepoint(HITRACE_CM_PROCESS, HITRACE_TP_CS, id, "client send msg content %d", 22); + HiTrace::Tracepoint(HITRACE_CM_THREAD, HITRACE_TP_CS, id, "client send msg content %d", 32); + HiTrace::Tracepoint(HITRACE_CM_DEFAULT, HITRACE_TP_CS, id, "client send msg content %d", 42); + + HiTrace::Tracepoint(HITRACE_TP_CS, id, "client send msg content %d", 13); + + HiTrace::End(id); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_TracepointTest_006 + * @tc.desc: Start trace without HITRACE_FLAG_D2D_TP_INFO, but with HITRACE_FLAG_TP_INFO flag. + * @tc.type: FUNC + * @tc.require: AR000CQVA3 + */ +HWTEST_F(HiTraceCppTest, TracepointTest_006, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with HITRACE_FLAG_TP_INFO flag. + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_D2D_TP_INFO is not enabled. + * * @tc.expected: step1. HITRACE_FLAG_TP_INFO is enabled. + * @tc.steps: step2. add D2D trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + * @tc.steps: step2. add trace point info with id and check logs. + * @tc.expected: step2. trace point can be found in logs. + */ + HiTraceId id = HiTrace::Begin("test no D2D, but tp flag", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(0, id.IsFlagEnabled(HITRACE_FLAG_D2D_TP_INFO)); + EXPECT_EQ(1, id.IsFlagEnabled(HITRACE_FLAG_TP_INFO)); + HiTrace::Tracepoint(HITRACE_CM_DEVICE, HITRACE_TP_CS, id, "client send msg content %d", 12); + HiTrace::Tracepoint(HITRACE_CM_PROCESS, HITRACE_TP_CS, id, "client send msg content %d", 22); + HiTrace::Tracepoint(HITRACE_CM_THREAD, HITRACE_TP_CS, id, "client send msg content %d", 32); + HiTrace::Tracepoint(HITRACE_CM_DEFAULT, HITRACE_TP_CS, id, "client send msg content %d", 42); + + HiTrace::Tracepoint(HITRACE_TP_CS, id, "client send msg content %d", 13); + + HiTrace::End(id); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_SyncAsyncTest_001 + * @tc.desc: Start trace with SYNC or ASYNC. + * @tc.type: FUNC + * @tc.require: AR000CQ0G7 + */ +HWTEST_F(HiTraceCppTest, SyncAsyncTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace without HITRACE_FLAG_INCLUDE_ASYNC flag. + * get and check flags. + * @tc.expected: step1. HITRACE_FLAG_INCLUDE_ASYNC is not enabled. + * @tc.steps: step2. start trace with HITRACE_FLAG_INCLUDE_ASYNC flag. + * get and check flags. + * @tc.expected: step2. HITRACE_FLAG_INCLUDE_ASYNC is enabled. + */ + /* begin with sync flag */ + HiTraceId syncId = HiTrace::Begin("test sync only", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(0, syncId.IsFlagEnabled(HITRACE_FLAG_INCLUDE_ASYNC)); + HiTrace::Tracepoint(HITRACE_TP_CS, syncId, "client send msg: %s", "sync"); + HiTrace::End(syncId); + /* begin with async flag */ + HiTraceId asyncId = HiTrace::Begin("test sync+async", HITRACE_FLAG_INCLUDE_ASYNC | HITRACE_FLAG_TP_INFO); + EXPECT_EQ(1, asyncId.IsFlagEnabled(HITRACE_FLAG_INCLUDE_ASYNC)); + HiTrace::Tracepoint(HITRACE_TP_CS, asyncId, "client send msg: %s", "async"); + + HiTrace::End(asyncId); +} + +/** + * @tc.name: Dfx_HiTraceCppTest_InvalidParamTest_001 + * @tc.desc: Start trace with SYNC or ASYNC. + * @tc.type: FUNC + * @tc.require: AR000CQV9U + */ +HWTEST_F(HiTraceCppTest, InvalidParamTest_001, TestSize.Level1) +{ + /** + * @tc.steps: step1. start trace with invalid flag and validate trace id. + * @tc.expected: step1. trace id is invalid. + * @tc.steps: step2. start trace with invalid name and validate trace id. + * @tc.expected: step2. trace id is valid. + */ + /* begin with invalid flag */ + HiTraceId invalidFlagId = HiTrace::Begin("invalid param", HITRACE_FLAG_MAX+1); + EXPECT_EQ(0, invalidFlagId.IsValid()); + invalidFlagId = HiTrace::Begin("invalid param", -1); + EXPECT_EQ(0, invalidFlagId.IsValid()); + HiTrace::End(invalidFlagId); + + /* begin with invalid name */ + HiTraceId invalidNameId = HiTrace::Begin("", HITRACE_FLAG_TP_INFO); + EXPECT_EQ(1, invalidNameId.IsValid()); + HiTrace::End(invalidNameId); +} + +} // namespace HiviewDFX +} // namespace OHOS diff --git a/interfaces/native/innerkits/BUILD.gn b/interfaces/native/innerkits/BUILD.gn new file mode 100644 index 0000000..7a73b64 --- /dev/null +++ b/interfaces/native/innerkits/BUILD.gn @@ -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. + +import("//build/ohos.gni") + +config("libhitrace_pub_config") { + visibility = [ ":*" ] + + include_dirs = [ "include" ] +} + +ohos_shared_library("libhitrace") { + public_configs = [ ":libhitrace_pub_config" ] + + deps = [ "//base/hiviewdfx/hitrace/frameworks/native:libhitrace_source" ] + + external_deps = [ "hilog_native:libhilog" ] + + output_extension = "so" + if (build_public_version) { + install_enable = true + } else { + install_enable = false + } + + part_name = "hitrace_native" + subsystem_name = "hiviewdfx" +} diff --git a/interfaces/native/innerkits/include/hitrace/hitrace.h b/interfaces/native/innerkits/include/hitrace/hitrace.h new file mode 100644 index 0000000..ae51637 --- /dev/null +++ b/interfaces/native/innerkits/include/hitrace/hitrace.h @@ -0,0 +1,49 @@ +/* + * 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 HIVIEWDFX_HITRACE_CPP_H +#define HIVIEWDFX_HITRACE_CPP_H + +#include "hitrace/hitrace.h" +#include "hitrace/hitraceid.h" + +#ifdef __cplusplus + +#include + +namespace OHOS { +namespace HiviewDFX { +class HiTrace final { +public: + static HiTraceId Begin(const std::string& name, int flags); + static void End(const HiTraceId& id); + static HiTraceId GetId(); + static void SetId(const HiTraceId& id); + static void ClearId(); + static HiTraceId CreateSpan(); + static void Tracepoint(HiTraceTracepointType type, const HiTraceId& id, const char* fmt, ...) + __attribute__((__format__(os_log, 3, 4))); + static void Tracepoint(HiTraceCommunicationMode mode, HiTraceTracepointType type, const HiTraceId& id, + const char* fmt, ...) __attribute__((__format__(os_log, 4, 5))); +private: + HiTrace() = default; + ~HiTrace() = default; +}; +} // namespace HiviewDFX +} // namespace OHOS + +#endif // __cplusplus + +#endif // HIVIEWDFX_HITRACE_CPP_H diff --git a/interfaces/native/innerkits/include/hitrace/hitracec.h b/interfaces/native/innerkits/include/hitrace/hitracec.h new file mode 100644 index 0000000..4841c81 --- /dev/null +++ b/interfaces/native/innerkits/include/hitrace/hitracec.h @@ -0,0 +1,245 @@ +/* + * 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 HIVIEWDFX_HITRACE_C_H +#define HIVIEWDFX_HITRACE_C_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum HiTraceIdValid { + HITRACE_ID_INVALID = 0, + HITRACE_ID_VALID = 1, +} HiTraceIdValid; + +typedef enum HiTraceVersion { + HITRACE_VER_1 = 0, +} HiTraceVersion; + +typedef enum HiTraceFlag { + // MIN: valid. + HITRACE_FLAG_MIN = 0, + // DEFAULT: default value. + HITRACE_FLAG_DEFAULT = 0, + // trace sync and async call. default: trace sync call only. + HITRACE_FLAG_INCLUDE_ASYNC = 1 << 0, + // do not create child span. default: create child span. + HITRACE_FLAG_DONOT_CREATE_SPAN = 1 << 1, + // output tracepoint info in span. default: do not output tracepoint info. + HITRACE_FLAG_TP_INFO = 1 << 2, + // do not output begin and end info. default: output begin and end info. + HITRACE_FLAG_NO_BE_INFO = 1 << 3, + // do not add id to log. default: add id to log. + HITRACE_FLAG_DONOT_ENABLE_LOG = 1 << 4, + // the trace is triggered by fault. + HITRACE_FLAG_FAULT_TRIGGER = 1 << 5, + // output device-to-device tracepoint info in span only. default: do not output device-to-device tracepoint info. + HITRACE_FLAG_D2D_TP_INFO = 1 << 6, + // MAX: valid. + HITRACE_FLAG_MAX = (1 << 7) - 1, +} HiTraceFlag; + +// HiTrace tracepoint type +typedef enum HiTraceTracepointType { + HITRACE_TP_MIN = 0, // MIN: valid + HITRACE_TP_CS = 0, // client send + HITRACE_TP_CR = 1, // client receive + HITRACE_TP_SS = 2, // server send + HITRACE_TP_SR = 3, // server receive + HITRACE_TP_GENERAL = 4, // general info + HITRACE_TP_MAX = 4, // MAX: valid +} HiTraceTracepointType; + +// HiTrace communication mode +typedef enum HiTraceCommunicationMode { + HITRACE_CM_MIN = 0, // MIN: valid + HITRACE_CM_DEFAULT = 0, // unspecified communication mode + HITRACE_CM_THREAD = 1, // thread-to-thread communication mode + HITRACE_CM_PROCESS = 2, // process-to-process communication mode + HITRACE_CM_DEVICE = 3, // device-to-device communication mode + HITRACE_CM_MAX = 3, // MAX: valid +} HiTraceCommunicationMode; + +typedef struct HiTraceIdStruct { +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint64_t valid : 1; + uint64_t ver : 3; + uint64_t chainId : 60; + + uint64_t flags : 12; + uint64_t spanId : 26; + uint64_t parentSpanId : 26; +#elif __BYTE_ORDER == __BIG_ENDIAN + uint64_t chainId : 60; + uint64_t ver : 3; + uint64_t valid : 1; + + uint64_t parentSpanId : 26; + uint64_t spanId : 26; + uint64_t flags : 12; +#else +#error "ERROR: No BIG_LITTLE_ENDIAN defines." +#endif +} HiTraceIdStruct; + +#define HITRACE_ID_LEN sizeof(HiTraceIdStruct) + +HiTraceIdStruct HiTraceBegin(const char* name, int flags); +void HiTraceEnd(const HiTraceIdStruct* pId); +HiTraceIdStruct HiTraceGetId(); +void HiTraceSetId(const HiTraceIdStruct* pId); +void HiTraceClearId(); +HiTraceIdStruct HiTraceCreateSpan(); +void HiTraceTracepoint(HiTraceTracepointType type, const HiTraceIdStruct* pId, const char* fmt, ...) + __attribute__((__format__(os_log, 3, 4))); +void HiTraceTracepointWithArgs(HiTraceTracepointType type, const HiTraceIdStruct* pId, const char* fmt, va_list args); +void HiTraceTracepointEx(HiTraceCommunicationMode mode, HiTraceTracepointType type, const HiTraceIdStruct* pId, + const char* fmt, ...) __attribute__((__format__(os_log, 4, 5))); +void HiTraceTracepointExWithArgs(HiTraceCommunicationMode mode, HiTraceTracepointType type, const HiTraceIdStruct* pId, + const char* fmt, va_list args); + +static inline void HiTraceInitId(HiTraceIdStruct* pId) +{ + pId->valid = HITRACE_ID_INVALID; + pId->ver = 0; + pId->chainId = 0; + pId->flags = 0; + pId->spanId = 0; + pId->parentSpanId = 0; +} + +static inline int HiTraceIsValid(const HiTraceIdStruct* pId) +{ + return (pId) && (pId->valid == HITRACE_ID_VALID); +} + +static inline int HiTraceIsFlagEnabled(const HiTraceIdStruct* pId, HiTraceFlag flag) +{ + return HiTraceIsValid(pId) && ((pId->flags & (uint64_t)flag) != 0); +} + +static inline void HiTraceEnableFlag(HiTraceIdStruct* pId, HiTraceFlag flag) +{ + if (HiTraceIsValid(pId)) { + pId->flags |= (uint64_t)flag; + } + return; +} + +static inline int HiTraceGetFlags(const HiTraceIdStruct* pId) +{ + if (!HiTraceIsValid(pId)) { + return 0; + } + return pId->flags; +} + +static inline void HiTraceSetFlags(HiTraceIdStruct* pId, int flags) +{ + if (HiTraceIsValid(pId) && (flags >= HITRACE_FLAG_MIN) && (flags < HITRACE_FLAG_MAX)) { + pId->flags = flags; + } + return; +} + +static inline uint64_t HiTraceGetChainId(const HiTraceIdStruct* pId) +{ + if (!HiTraceIsValid(pId)) { + return 0; + } + return pId->chainId; +} + +static inline void HiTraceSetChainId(HiTraceIdStruct* pId, uint64_t chainId) +{ + if (!pId) { + return; + } + + if (!HiTraceIsValid(pId)) { + pId->valid = HITRACE_ID_VALID; + pId->ver = HITRACE_VER_1; + pId->flags = pId->spanId = pId->parentSpanId = 0; + } + pId->chainId = chainId; +} + +static inline uint64_t HiTraceGetSpanId(const HiTraceIdStruct* pId) +{ + if (!HiTraceIsValid(pId)) { + return 0; + } + return pId->spanId; +} + +static inline void HiTraceSetSpanId(HiTraceIdStruct* pId, uint64_t spanId) +{ + if (HiTraceIsValid(pId)) { + pId->spanId = spanId; + } + return; +} + +static inline uint64_t HiTraceGetParentSpanId(const HiTraceIdStruct* pId) +{ + if (!HiTraceIsValid(pId)) { + return 0; + } + return pId->parentSpanId; +} + +static inline void HiTraceSetParentSpanId(HiTraceIdStruct* pId, uint64_t parentSpanId) +{ + if (HiTraceIsValid(pId)) { + pId->parentSpanId = parentSpanId; + } + return; +} + +static inline int HiTraceIdToBytes(const HiTraceIdStruct* pId, uint8_t* pIdArray, int len) +{ + if (!HiTraceIsValid(pId) || (len < (int)HITRACE_ID_LEN)) { + return 0; + } + + *((uint64_t*)pIdArray) = htobe64(*((uint64_t*)pId)); + *((uint64_t*)pIdArray + 1) = htobe64(*((uint64_t*)pId + 1)); + return sizeof(HiTraceIdStruct); +} + +static inline HiTraceIdStruct HiTraceBytesToId(const uint8_t* pIdArray, int len) +{ + HiTraceIdStruct id = {0, 0, 0, 0, 0, 0}; + HiTraceInitId(&id); + + if ((!pIdArray) || (len != (int)HITRACE_ID_LEN)) { + return id; + } + + *((uint64_t*)(&id)) = be64toh(*((uint64_t*)pIdArray)); + *((uint64_t*)(&id) + 1) = be64toh(*((uint64_t*)pIdArray + 1)); + return id; +} + +#ifdef __cplusplus +} +#endif + +#endif // HIVIEWDFX_HITRACE_C_H diff --git a/interfaces/native/innerkits/include/hitrace/hitraceid.h b/interfaces/native/innerkits/include/hitrace/hitraceid.h new file mode 100644 index 0000000..4ed9818 --- /dev/null +++ b/interfaces/native/innerkits/include/hitrace/hitraceid.h @@ -0,0 +1,54 @@ +/* + * 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 HIVIEWDFX_HITRACE_ID_H +#define HIVIEWDFX_HITRACE_ID_H + +#include "hitrace/hitracec.h" + +#ifdef __cplusplus + +namespace OHOS { +namespace HiviewDFX { +class HiTraceId final { +public: + HiTraceId(); + HiTraceId(const HiTraceIdStruct& id); + HiTraceId(const uint8_t* pIdArray, int len); + ~HiTraceId() = default; + + bool IsValid() const; + bool IsFlagEnabled(HiTraceFlag flag) const; + void EnableFlag(HiTraceFlag flag); + int GetFlags() const; + void SetFlags(int flags); + uint64_t GetChainId() const; + void SetChainId(uint64_t chainId); + uint64_t GetSpanId() const; + void SetSpanId(uint64_t spanId); + uint64_t GetParentSpanId() const; + void SetParentSpanId(uint64_t parentSpanId); + int ToBytes(uint8_t* pIdArray, int len) const; + +private: + HiTraceIdStruct id_; + friend class HiTrace; +}; +} // namespace HiviewDFX +} // namespace OHOS + +#endif // __cplusplus + +#endif // HIVIEWDFX_HITRACE_ID_H diff --git a/interfaces/native/innerkits/include/hitrace/trace.h b/interfaces/native/innerkits/include/hitrace/trace.h new file mode 100644 index 0000000..b6406d5 --- /dev/null +++ b/interfaces/native/innerkits/include/hitrace/trace.h @@ -0,0 +1,23 @@ +/* + * 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 HIVIEWDFX_HITRACE_H +#define HIVIEWDFX_HITRACE_H + +#include "hitrace/hitrace.h" +#include "hitrace/hitracec.h" +#include "hitrace/hitraceid.h" + +#endif // HIVIEWDFX_HITRACE_H \ No newline at end of file diff --git a/lite/BUILD.gn b/lite/BUILD.gn new file mode 100644 index 0000000..dbc2ebe --- /dev/null +++ b/lite/BUILD.gn @@ -0,0 +1,43 @@ +# Copyright (c) 2020 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/lite/config/component/lite_component.gni") +import("//build/lite/ndk/ndk.gni") + +hitrace_sources = [ + "//base/hiviewdfx/hitrace/frameworks/native/hitrace.cpp", + "//base/hiviewdfx/hitrace/frameworks/native/hitracec.c", + "//base/hiviewdfx/hitrace/frameworks/native/hitraceid.cpp" +] + +config("hitrace_config") { + include_dirs = [ + "//base/hiviewdfx/hitrace/interfaces/native/innerkits", + "//base/hiviewdfx/hilog_lite/interfaces/native/innerkits", + "//base/hiviewdfx/hilog_lite/interfaces/native/innerkits/hilog", + "//base/hiviewdfx/hitrace/interfaces/native/innerkits/include", + "//third_party/bounds_checking_function/include", + ] +} + +lite_library("hitrace") { + target_type = "shared_library" + sources = hitrace_sources + public_configs = [":hitrace_config"] + public_deps = [ + "//third_party/bounds_checking_function:libsec_shared", + "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared" + ] + cflags = [ "-Wall" ] +} + diff --git a/lite/test/BUILD.gn b/lite/test/BUILD.gn new file mode 100644 index 0000000..71c698a --- /dev/null +++ b/lite/test/BUILD.gn @@ -0,0 +1,68 @@ +# 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/lite/config/component/lite_component.gni") +import("//build/lite/config/test.gni") + +hitrace_root_dir="//base/hiviewdfx/hitrace" +hitrace_code_dir="${hitrace_root_dir}/frameworks/native" +hitrace_test_dir="${hitrace_root_dir}/frameworks/native/test/unittest" + +config("module_private_config") { + visibility = [ ":*" ] + include_dirs = [ + "//base/hiviewdfx/hitrace/interfaces/native/innerkits/include", + "//base/hiviewdfx/hilog_lite/interfaces/native/innerkits", + "//base/hiviewdfx/hilog_lite/interfaces/native/innerkits/hilog", + "//third_party/bounds_checking_function/include", + ] +} + +unittest("HitraceCTest") { + sources = [ + "${hitrace_code_dir}/hitracec.c", + "${hitrace_test_dir}/common/hitracec_test.cpp", + ] + + configs = [ ":module_private_config" ] + + public_deps = [ + "//third_party/bounds_checking_function:libsec_shared", + "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared" + ] +} + +unittest("HitraceCppTest") { + sources = [ + "${hitrace_code_dir}/hitrace.cpp", + "${hitrace_code_dir}/hitracec.c", + "${hitrace_code_dir}/hitraceid.cpp", + "${hitrace_test_dir}/common/hitracecpp_test.cpp", + ] + + configs = [ ":module_private_config" ] + + public_deps = [ + "//third_party/bounds_checking_function:libsec_shared", + "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared" + ] +} + +group("unittest") { + if (ohos_build_type == "debug") { + deps = [ + ":HitraceCTest", + ":HitraceCppTest", + ] + } +} diff --git a/ohos.build b/ohos.build new file mode 100644 index 0000000..ee0757f --- /dev/null +++ b/ohos.build @@ -0,0 +1,24 @@ +{ + "subsystem": "hiviewdfx", + "parts": { + "hitrace_native": { + "module_list": [ + "//base/hiviewdfx/hitrace/interfaces/native/innerkits:libhitrace" + ], + "inner_kits": [ + { + "name": "//base/hiviewdfx/hitrace/interfaces/native/innerkits:libhitrace", + "header": { + "header_base": "//base/hiviewdfx/hitrace/interfaces/native/innerkits/include/", + "header_files": [ + "hitrace/trace.h", + "hitrace/hitracec.h", + "hitrace/hitraceid.h", + "hitrace/hitrace.h" + ] + } + } + ] + } + } +}