#夏日挑战赛# OpenHarmony HiTrace在IPC通信中的应用(L2) 原创 精华
@toc
简介
HiTrace主要是对于跨设备/跨进程/跨线程的业务流程,通过相同的traceid在整个业务流程中传递,将流程处理过程中的调用关系、各种输出信息关联和展现出来,帮助使用者分析、定位问题和系统调优。此文章以OpenHarmony 3.1代码基础, 以IPC进程通信中的HiTrace应用介绍HiTrace的使用和数据流转。
- HiTrace在IPC通信中的应用介绍。
- HiTrace在IPC通信中应用示例。
- HiTrace在IPC中的数据流介绍。
- 本用例开发板(3516开发板:HiSpark_AI_Hi3516D_One_Light_VER.B开发板上测试)
HiTrace在IPC通信中的应用介绍。
IPC通信的框架代码中:
客户端的发送接收数据地方已经有 tranceId发送到服务端以及发送和接收数据的跟踪日志代码。
服务端的接收回复数据地方已经有 获取传递过来的tranceId以及接收和发送数据的跟踪日志代码。
我们只需要在要跟踪的代码块前添加HiTrace::Begin, 代码块后添加HiTrace::End即可。
抓取跟踪日志:hilog | grep HiTraceC
查看数据的发送和接收日志。
- 源代码开发 在类定义头文件或者类实现源文件中,包含hitrace头文件:
#include "hitrace/trace.h"
- 在业务类实现源文件中使用(启动/结束跟踪):
using namespace OHOS::HiviewDFX;
HiTraceId traceId = HiTrace::Begin("MyServiceFlow", HITRACE_FLAG_DEFAULT);
......
HiTrace::End(traceId);
- 跟踪日志(IPC框架中已经实现,不用实现)
HiTraceId id = HiTrace::GetId(); // 在Begin和End之间的id是有效的,其他阶段id无效。id无效,则Tracepoint的日志不会输出。
HiTrace::Tracepoint(HITRACE_TP_CS, id, "client send msg content"); // hilog | grep HiTraceC 抓取跟踪日志
- 编译设置,在BUILD.gn里增加子系统SDK依赖:
external_deps = [ "hiviewdfx:libhitrace" ]
HiTrace在IPC通信中应用示例。
子系统配置
build\subsystem_config.json
"myapp": {
"path":"myapptest",
"name": "myapp"
}
产品配置
productdefine\common\products\Hi3516DV300.json
"myapp:myappservice_test":{}
代码
代码目录结构
myapptest放在代码根目录,代码见附件
服务ID的添加
服务ID有统一的头文件
foundation\distributedschedule\samgr\interfaces\innerkits\samgr_proxy\include\system_ability_definition.h
MY_APP_SERVICE_ID = 9000,
...
{ MY_APP_SERVICE_ID, "MyAppService"},
编译
编译
和把编译结果文件发送到开发板
,修改权限
参考OpenHarmony 实现的一个IPC的客户端和服务端, 代码基于这个文档的代码修改添加。
测试
终端1:抓取日志
hilog | grep HiTraceC
图中白色部分是一次完整的客户端和服务端交互流程, 每一个begin和end之间tranceid相同,每一次完整的交互spanid一样。CS:客户端发送数据,CR:客户端接收数据,SR:服务端接收数据,SS:服务端发送数据。
终端2:启动服务
sa_main /system/profile/myappservice_sa.xml
终端3:启动客户端
/data/test/myappclient
HiTrace在IPC中的数据流介绍
入口函数文件:
foundation\communication\ipc\ipc\native\src\mock\source\binder_invoker.cpp
IPC客户端发收数据入口:
int BinderInvoker::SendRequest(int handle, uint32_t code, MessageParcel &data, MessageParcel &reply,
MessageOption &option)
{
int error = ERR_NONE;
uint32_t flags = (uint32_t)option.GetFlags();
ZLOGI(LABEL, "%{public}s: handle=%d ,flags:%u, code:%u", __func__, handle, flags, code);
MessageParcel &newData = const_cast<MessageParcel &>(data);
size_t oldWritePosition = newData.GetWritePosition();
// ####### 1.获取tranceId
HiTraceId traceId = HiTrace::GetId();
// set client send trace point if trace is enabled
// ####### 2.客户端数据发送跟踪记录,并把tranceid打包到发送数据中去。
HiTraceId childId = HitraceInvoker::TraceClientSend(handle, code, newData, flags, traceId);
if (!WriteTransaction(BC_TRANSACTION, flags, handle, code, data, nullptr)) {
newData.RewindWrite(oldWritePosition);
ZLOGE(LABEL, "WriteTransaction ERROR");
#ifndef BUILD_PUBLIC_VERSION
ReportDriverEvent(DbinderErrorCode::COMMON_DRIVER_ERROR, DbinderErrorCode::ERROR_TYPE,
DbinderErrorCode::IPC_DRIVER, DbinderErrorCode::ERROR_CODE, DbinderErrorCode::TRANSACT_DATA_FAILURE);
#endif
return IPC_INVOKER_WRITE_TRANS_ERR;
}
if ((flags & TF_ONE_WAY) != 0) {
error = WaitForCompletion(nullptr);
} else {
error = WaitForCompletion(&reply);
}
// ####### 3.客户端数据接收跟踪记录。
HitraceInvoker::TraceClientReceieve(handle, code, flags, traceId, childId);
// restore Parcel data
newData.RewindWrite(oldWritePosition);
ZLOGI(LABEL, "%{public}s: handle=%d result = %{public}d", __func__, handle, error);
return error;
}
服务端收发数据入口:
void BinderInvoker::OnTransaction(const uint8_t *buffer)
{
const binder_transaction_data *tr = reinterpret_cast<const binder_transaction_data *>(buffer);
auto data = std::make_unique<MessageParcel>(new BinderAllocator());
data->ParseFrom(tr->data.ptr.buffer, tr->data_size);
if (tr->offsets_size > 0) {
data->InjectOffsets(tr->data.ptr.offsets, tr->offsets_size / sizeof(binder_size_t));
}
uint32_t &newflags = const_cast<uint32_t &>(tr->flags);
// ####### 1.服务端数据接收数据,解析tranceid,记录接收数据的跟踪信息。
int isServerTraced = HitraceInvoker::TraceServerReceieve(tr->target.handle, tr->code, *data, newflags);
const pid_t oldPid = callerPid_;
const auto oldUid = static_cast<const uid_t>(callerUid_);
const uint32_t oldToken = callerTokenID_;
const uint32_t oldFirstToken = firstTokenID_;
uint32_t oldStatus = status_;
callerPid_ = tr->sender_pid;
callerUid_ = tr->sender_euid;
if (binderConnector_->IsAccessTokenSupported()) {
struct access_token tmp;
int error = binderConnector_->WriteBinder(BINDER_GET_ACCESS_TOKEN, &tmp);
if (error != ERR_NONE) {
callerTokenID_ = 0;
firstTokenID_ = 0;
} else {
callerTokenID_ = tmp.sender_tokenid;
firstTokenID_ = tmp.first_tokenid;
}
} else {
callerTokenID_ = 0;
firstTokenID_ = 0;
}
SetStatus(IRemoteInvoker::ACTIVE_INVOKER);
int error = ERR_DEAD_OBJECT;
sptr<IRemoteObject> targetObject;
if (tr->target.ptr != 0) {
auto *refs = reinterpret_cast<IRemoteObject *>(tr->target.ptr);
if ((refs != nullptr) && (tr->cookie) && (refs->AttemptIncStrongRef(this))) {
targetObject = reinterpret_cast<IPCObjectStub *>(tr->cookie);
targetObject->DecStrongRef(this);
}
} else {
targetObject = IPCProcessSkeleton::GetCurrent()->GetRegistryObject();
}
MessageParcel reply;
MessageOption option;
uint32_t flagValue = static_cast<uint32_t>(tr->flags) & ~static_cast<uint32_t>(MessageOption::TF_ACCEPT_FDS);
if (targetObject != nullptr) {
option.SetFlags(static_cast<int>(flagValue));
error = targetObject->SendRequest(tr->code, *data, reply, option);
}
// ####### 2.服务端数据回复记录接发送数据的跟踪信息。
HitraceInvoker::TraceServerSend(tr->target.handle, tr->code, isServerTraced, newflags);
if (!(flagValue & TF_ONE_WAY)) {
SendReply(reply, 0, error);
}
callerPid_ = oldPid;
callerUid_ = oldUid;
callerTokenID_ = oldToken;
firstTokenID_ = oldFirstToken;
SetStatus(oldStatus);
}
这里只对IPC数据交互的客户端和服务端总入口进行简单注释。要对IPC框架了解,需要结合OpenHarmony IPC通信(L2)进行代码分析学习。
很详细的讲解!
虽然看不懂,但还是感觉很牛的样子。
大佬优秀啊
666
大佬优秀啊
很不错,赞起!