1 关键字

Acts兼容性测评;冷启动测试;

2 问题描述

OpenHarmony版本:3.2 Release

问题描述:使用 Acts-Validator 套件进行冷启动兼容性测试失败

测试步骤:

  1. OpenHarmony 兼容性测试网站下载对应的 Acts-Validator 测试套件;

  2. 开发板安装 ActsValidatorTest.hap 应用;

  3. 推送下载的测试资源到开发板;

  4. 点击桌面 ActsValidatorTest 应用图标打开应用,Window系统电脑端点击.bat文件启动测试;

  5. 按照 ActsValidatorTest 应用内提示执行冷启动性能测试

3 问题原因

3.1 正常机制

冷启动兼容性测试正常测试,并生成对应结果

3.2 异常机制

按照步骤进行冷启动兼容性测试测试,无法采集冷启动时间,目录下的 ColdStartSettings.log 生成如下日志:

璁剧疆 Duplicate Application Name

产生该问题的原因有2点:

  1. 社区发布的测试插件没有适配非rk3568 的产品

  2. 由于该款芯片性能较差,执行测试命令后,拉起应用响应太慢,导致分析 trace 过程出错,无法获取启动时长

4 解决方案

修改性能 SP_daemon 插件源码,重新编译 SP_daemon 插件

5 定位过程

使用 ActsValidatorTest.hap 进行冷启动测试,没有生成对应的测试数据,于是采用命令行的形式,进行测试定位。命令行指令如下:

hdc shell SP_daemon -editor com.ohos.settings 璁剧疆

执行上述命令后,在终端依旧显示:璁剧疆 Duplicate Application Name。

查看 SP_daemon 工具的源代码,找到 Duplicate Application Name 日志打印的位置。

EditorCommand::EditorCommand(int argc, std::vector<std::string> v)
{
    if (argc >= threeParamMore) {
        ......
        if (v[type] == "coldStart") {
            time = SmartPerf::EditorCommand::ColdStart(v);
        } else if (v[type] == "hotStart") {
            time = SmartPerf::EditorCommand::HotStart(v);
        } else if (v[type] == "responseTime") {
            time = SmartPerf::EditorCommand::ResponseTime();
        } else if (v[type] == "completeTime") {
            time = SmartPerf::EditorCommand::CompleteTime();
        }
        if (time == noNameType) {
            std::cout << v[typeName] << " Duplicate Application Name" << std::endl;
        } else {
            std::cout << "time:" << time << std::endl;
        }
    }
}
​

代码路径:developtools/profiler/host/smartperf/client/client_command/editor_command.cpp

根据以上代码,可以确定,ColdStart 方法返回值为 noNameType,会打印该log。

进入同文件的 ColdStart 方法,增加 log 打印,编译 SP 工具,再次进行测试。

编译命令:

./build.sh --product-name 产品名 --build-target SP_daemon
float EditorCommand::ColdStart(std::vector<std::string> v)
{
    std::cout << "ColdStart111"  << std::endl;
    OHOS::SmartPerf::StartUpDelay sd;
    OHOS::SmartPerf::ParseTrace parseTrace;
    std::string cmdResult;
    int type = 4;
    int typePKG = 3;
    float noNameType = 5.0;
    SPUtils::LoadCmd("rm -rfv /data/local/tmp/*.json", cmdResult);
    SPUtils::LoadCmd("rm -rfv /data/local/tmp/*.ftrace", cmdResult);
    SPUtils::LoadCmd("uitest dumpLayout", cmdResult);
    sleep(1);
    size_t position = cmdResult.find(":");
    std::string pathJson = cmdResult.substr(position + 1);
    sd.InitXY2(v[type], pathJson, v[typePKG]);
    std::cout << "ColdStart222"  << std::endl;
    if (sd.pointXY == "0 0") {
        std::cout << "ColdStart333"  << std::endl;
        return noNameType;
    } else {
        std::string traceName = std::string("/data/local/tmp/") + std::string("sp_trace_") + "coldStart" + ".ftrace";
        std::thread thGetTrace = sd.ThreadGetTrace("coldStart", traceName);
        std::string cmd = "uinput -T -d " + sd.pointXY + " -u " + sd.pointXY;
        sleep(1);
        SPUtils::LoadCmd(cmd, cmdResult);
        std::string pid = sd.GetPidByPkg(v[typePKG]);
        thGetTrace.join();
        std::string deviceType = sd.GetDeviceType();
        float time = 0.0;
        std::cout << "ColdStart444"  << std::endl;
        if (deviceType == " rk3568") {
            std::cout << "ColdStart555"  << std::endl;
            time = parseTrace.ParseTraceCold(traceName, pid);
        } else {
            std::cout << "ColdStart666"  << std::endl;
            time = parseTrace.ParseTraceNoah(traceName, pid);
        }
        std::cout << "ColdStart777"  << std::endl;
        return time;
    }
}

再次测试后,生成了如下log:

ColdStart111
ColdStart222
ColdStart444
ColdStart666
ColdStart777
璁剧疆 Duplicate Application Name

根据日志,可以发现,代码最终调用了 ParseTraceNoah 方法。正确的调用,应该是进入 rk3568 的代码分支。 为了适配社区非 rk3568 对应的产品,对分支条件进行如下修改:增加标识位,用来适配社区非 rk3568 对应的产品。

EditorCommand::EditorCommand(int argc, std::vector<std::string> v)
{
    if (argc >= threeParamMore) {
        int ohType = 5;
        int type = 2;
        float time = 0.0;
        float noNameType = -1.0;
        if (v[ohType] == "ohtest") {
            isOhTest = true;
        }
        if (v[type] == "coldStart") {
            time = SmartPerf::EditorCommand::ColdStart(v);
        } 
    }
    ......
}
​
float EditorCommand::ColdStart(std::vector<std::string> v)
{
   ......
    if (sd.pointXY == "0 0") {
        return noNameType;
    } else {
       ......
        if (isOhTest) {
            time = parseTrace.ParseTraceCold(traceName, pid);
        } else {
            time = parseTrace.ParseTraceNoh(traceName, pid);
        }
        return time;
    }
}

对应的测试命令,需要增加如下参数:

hdc_std shell SP_daemon -editor com.ohos.settings 璁剧疆 ohtest

再次运行后,发现依旧没有采集到冷启动时间,产生的同样的log。于是进入 ParseTraceCold 分析。

ParseTraceCold 方法会解析采集到的 trace 文件,找到 "H:touchEventDispatch" 和 "tracing_mark_write: B|"+ appPid + "|H:RSRenderThread DrawFrame:" 这2个节点,分别计算出 startTime 和 endTime,然后计算差值。

在 ParseCodeTrace 方法中增加对应的log

float ParseTrace::ParseCodeTrace(const std::string &fileNamePath, const std::string &appPid)
    {   
        ::string line;
        ::string::size_type tracingMarkWrite;
        ::string::size_type fourPoint;
        at codeTime = -1;
        le (getline(infile, line)) {
            startTime = SmartPerf::ParseTrace::GetStartTime(line, startTime);
            std::cout << "startTime" << startTime << std::endl;
            tracingMarkWrite = line.find("tracing_mark_write: B|"+ appPid + "|H:RSRenderThread DrawFrame:");
            std::cout << "tracingMarkWrite" << tracingMarkWrite << std::endl;
            fourPoint = line.find("....");
            if (tracingMarkWrite != std::string::npos && fourPoint != std::string::npos) {
                size_t p1 = line.find("....");
                size_t p2 = line.find(":");
                size_t subNum = 5;
                endTime = line.substr(p1 + subNum, p2 - p1 - subNum);
                std::cout << "endTime" << endTime << std::endl;
                int endNum = std::stof(endTime);
                int endFlagNum = std::stof(endTimeFlag);
                int startNum = std::stof(startTime);
                int timeNum = endNum - endFlagNum;
                float interval = 0.3;
                if (timeNum < interval) {
                        endTimeFlag = endTime;
                } else {
                    if (std::stof(endTimeFlag) == 0) {
                        endTimeFlag = endTime;
                    } else if (endFlagNum != 0 && startNum != 0 && timeNum > interval) {
                        break;
                    } else {
                        endTimeFlag = endTime;
                    }
                }
            }
        }
        codeTime = SmartPerf::ParseTrace::GetTime(startTime, endTime);
        std::cout << "codeTime" << codeTime << std::endl;
        return codeTime;
    }

代码路径:developtools/profiler/host/smartperf/client/client_command/parse_trace.cpp

再次运行后,产生如下日志:

startTime17518
tracingMarkWrite
codeTime
璁剧疆 Duplicate Application Name

根据日志,可以看到 tracingMarkWrite 为空,于是导出开发板中的产生的 trace 文件,并使用文本编辑器打开,查找"tracing_mark_write: B|" 和 "|H:RSRenderThread DrawFrame:" 字符串。

导出命令:

hdc_std shell file recv /data/local/tmp/sp_trace_coldStart.ftrace 本地路径

使用文本编辑打开导出的 trace 文件后,可以在 trace 文件中找到 tracing_mark_write 和 H:RSRenderThread DrawFrame 节点。

于是再次增加 log 日志,打印对应的appPid。

再次运行后,产生如下日志:可以发现,appid 为空。

appPid
璁剧疆 Duplicate Application Name

pid 是在 ColdStart 方法中调用 startup_delay.cpp 文件中的 GetPidByPkg 方法获取的。

std::string StartUpDelay::GetPidByPkg(const std::string &curPkgName)
{
    std::string resultPid;
    SPUtils::LoadCmd("pidof " + curPkgName, resultPid);
    return resultPid;
}

代码路径:developtools/profiler/host/smartperf/client/client_command/startup_delay.cpp

于是在终端中执行如下命令,测试能否拿到pid。

hdc_std shell pidof com.ohos.settings

执行后,查看终端输出,可以正确获取对应的pid。

C:User\xxx\hdc_std shell pidof com.ohos.settings
3505

命令可以获取pid,但是在插件工具却无法获取,于是再次分析 ColdStart 方法。

float EditorCommand::ColdStart(std::vector<std::string> v)
{
   ......
   ...
    if (sd.pointXY == "0 0") {
        return noNameType;
    } else {
        std::string traceName = std::string("/data/local/tmp/") + std::string("sp_trace_") + "coldStart" + ".ftrace";
        std::thread thGetTrace = sd.ThreadGetTrace("coldStart", traceName);
        std::string cmd = "uinput -T -d " + sd.pointXY + " -u " + sd.pointXY;
        sleep(1);
        SPUtils::LoadCmd(cmd, cmdResult);
        std::string pid = sd.GetPidByPkg(v[typePKG]);
        ......
        return time;
    }
}

LoadCmd 方法执行 "uinput -T -d" 指令模拟点击打开应用后,马上调用 "pidof" 指令获取 pid,这个时刻,由于该款芯片性能较差,应用的进程还没有孵化,此时无法获取到对应的pic。于是在执行"uinput -T -d"指令后,增加1秒延时。修改后代码如下:

float EditorCommand::ColdStart(std::vector<std::string> v)
{
    ......
    if (sd.pointXY == "0 0") {
        return noNameType;
    } else {
        std::string traceName = std::string("/data/local/tmp/") + std::string("sp_trace_") + "coldStart" + ".ftrace";
        std::thread thGetTrace = sd.ThreadGetTrace("coldStart", traceName);
        std::string cmd = "uinput -T -d " + sd.pointXY + " -u " + sd.pointXY;
        sleep(1);
        SPUtils::LoadCmd(cmd, cmdResult);
        sleep(1); // 增加1秒延时
        std::string topPkg = SPUtils::GetTopPkgName();
        std::string pid = sd.GetPidByPkg(v[typePKG]);
        thGetTrace.join();
        if (topPkg.find(v[typePKG]) == std::string::npos || pid == "") {
            return noNameType;
        }
        float time = 0.0;
        if (isOhTest) {
            time = parseTrace.ParseTraceCold(traceName, pid);
        } else {
            time = parseTrace.ParseTraceNoh(traceName, pid);
        }
        return time;
    }
}

再次编译,执行命令,可以正常获取应用的启动时长。

C:User\xxx\hdc_std shell SP_daemon -editor com.ohos.settings 璁剧疆 ohtest
time:1408.45

再次使用测试套件测试后,ActsValidatorTest 能够正确获取冷启动时间,生成的 ColdStartSettings.log 文件正确记录对应的值。

  • 备注:最新的测试套件已经上传至OpenHarmony兼容性测评官网,修改后的代码已经同步更新至社区developtools_profiler仓库。

6 知识分享

OpenHarmony兼容性测评主要是验证合作伙伴的设备和业务应用满足OpenHarmony开源兼容性定义的技术要求,确保运行在OpenHarmony上的设备和业务应用能稳定、正常运行,同时使用OpenHarmony的设备和业务应用有一致性的接口和业务体验。

OpenHarmony兼容性测评服务包括:产品兼容性技术规范文档与兼容性测试两部分。

兼容性测试包括:acts、acts-validator、hats、dcts、ssts

OpenHarmony兼容性测试需要合作伙伴获取代码和兼容性测试套,并完成自测,取得兼容性测试报告后,在测试流程上传兼容性测试报告供开放原子开源基金会团队进行审核或抽测。兼容性测试的套件范围:

acts(application compatibility test suite)应用兼容性测试套件,看护北向HAP兼容、OpenHarmony开发API兼容。

acts-validator 应用兼容性补充测试套件,需要根据引导完成手工测试。

hats(Hardware Abstraction Test Suite )硬件抽象兼容性测试套,看护HDI层接口。

dcts(Distributed Compatibility Test Suite )分布式兼容性测试套,看护分布式兼容性。

ssts(System Security Test Suite )系统安全漏洞测试套,看护已知系统安全漏洞补丁的修复情况。

更多兼容性测评请参考

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐