1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include "hidump_plugin.h"
16#include <sys/syscall.h>
17#include <sys/types.h>
18#include <unistd.h>
19#include <cstdio>
20#include <cstring>
21#include <fcntl.h>
22#include <cinttypes>
23#include <csignal>
24#include <sstream>
25#include <sys/wait.h>
26
27#include "hidump_plugin_result.pbencoder.h"
28#include "securec.h"
29#include "common.h"
30
31namespace {
32using namespace OHOS::Developtools::Profiler;
33const int SLEEP_TIME = 50;
34const int BUF_MAX_LEN = 64;
35const int MS_PER_S = 1000;
36const int US_PER_S = 1000000;
37const char *FPS_FORMAT = "SP_daemon -profilerfps 31104000 -sections 10";
38
39} // namespace
40
41HidumpPlugin::HidumpPlugin() : fp_(nullptr, nullptr) {}
42
43HidumpPlugin::~HidumpPlugin()
44{
45    PROFILER_LOG_INFO(LOG_CORE, "%s: ready!", __func__);
46    std::unique_lock<std::mutex> locker(mutex_);
47    if (running_) {
48        running_ = false;
49        if (writeThread_.joinable()) {
50            writeThread_.join();
51        }
52    }
53    locker.unlock();
54
55    if (fp_ != nullptr) {
56        fp_.reset();
57    }
58    PROFILER_LOG_INFO(LOG_CORE, "%s: success!", __func__);
59}
60
61int HidumpPlugin::Start(const uint8_t* configData, uint32_t configSize)
62{
63    PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin:Start ----> !");
64    CHECK_TRUE(protoConfig_.ParseFromArray(configData, configSize) > 0, -1, "HidumpPlugin: ParseFromArray failed");
65    std::vector<std::string> fullCmd;
66    fullCmd.push_back("/system/bin/SP_daemon");
67    fullCmd.push_back("SP_daemon");
68    fullCmd.push_back("-profilerfps");
69    fullCmd.push_back("31104000");
70    fullCmd.push_back("-sections");
71    fullCmd.push_back(std::to_string(protoConfig_.sections()));
72    fp_ = std::unique_ptr<FILE, std::function<int (FILE*)>>(
73        COMMON::CustomPopen(fullCmd, "r", pipeFds_, childPid_, true), [this](FILE* fp) -> int {
74            return COMMON::CustomPclose(fp, pipeFds_, childPid_, true);
75        });
76    if (fp_.get() == nullptr) {
77        const int bufSize = 256;
78        char buf[bufSize] = {0};
79        strerror_r(errno, buf, bufSize);
80        PROFILER_LOG_ERROR(LOG_CORE, "HidumpPlugin: CustomPopen(%s) Failed, errno(%d:%s)", FPS_FORMAT, errno, buf);
81        return -1;
82    }
83    CHECK_NOTNULL(resultWriter_, -1, "HidumpPlugin: Writer is no set!");
84    CHECK_NOTNULL(resultWriter_->write, -1, "HidumpPlugin: Writer.write is no set!");
85    CHECK_NOTNULL(resultWriter_->flush, -1, "HidumpPlugin: Writer.flush is no set!");
86    std::unique_lock<std::mutex> locker(mutex_);
87    running_ = true;
88    writeThread_ = std::thread([this] { this->Loop(); });
89    PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: ---> Start success!");
90    return 0;
91}
92
93int HidumpPlugin::Stop()
94{
95    std::unique_lock<std::mutex> locker(mutex_);
96    running_ = false;
97    locker.unlock();
98    if (writeThread_.joinable()) {
99        writeThread_.join();
100    }
101    PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin:stop thread success!");
102    if (fp_ != nullptr) {
103        fp_.reset();
104    }
105    PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: stop success!");
106    return 0;
107}
108
109int HidumpPlugin::SetWriter(WriterStruct* writer)
110{
111    resultWriter_ = writer;
112    return 0;
113}
114
115void HidumpPlugin::Loop(void)
116{
117    PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: Loop start");
118    CHECK_NOTNULL(resultWriter_, NO_RETVAL, "%s: resultWriter_ nullptr", __func__);
119
120    fcntl(fileno(fp_.get()), F_SETFL, O_NONBLOCK);
121    while (running_) {
122        char buf[BUF_MAX_LEN] = { 0 };
123
124        if (fgets(buf, BUF_MAX_LEN - 1, fp_.get()) == nullptr) {
125            std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));
126            continue;
127        }
128        char* pTempBuf = buf;
129        if (strncmp(pTempBuf, "fps:", strlen("fps:")) == 0) {
130            pTempBuf += strlen("fps:");
131            std::string stringBuf(pTempBuf);
132            size_t npos = stringBuf.find("|");
133            uint32_t fps = static_cast<uint32_t>(std::stoi(stringBuf.substr(0, npos)));
134            if (fps > 0) {
135                continue;
136            }
137        }
138        if (resultWriter_->isProtobufSerialize) {
139            HidumpInfo dataProto;
140            if (!ParseHidumpInfo(dataProto, buf, sizeof(buf))) {
141                continue;
142            }
143            if (dataProto.ByteSizeLong() > 0) {
144                buffer_.resize(dataProto.ByteSizeLong());
145                dataProto.SerializeToArray(buffer_.data(), buffer_.size());
146                resultWriter_->write(resultWriter_, buffer_.data(), buffer_.size());
147                resultWriter_->flush(resultWriter_);
148            }
149        } else {
150            ProtoEncoder::HidumpInfo hidumpInfo(resultWriter_->startReport(resultWriter_));
151            if (!ParseHidumpInfo(hidumpInfo, buf, sizeof(buf))) {
152                PROFILER_LOG_ERROR(LOG_CORE, "parse hidump info failed!");
153            }
154            int messageLen = hidumpInfo.Finish();
155            resultWriter_->finishReport(resultWriter_, messageLen);
156            resultWriter_->flush(resultWriter_);
157        }
158    }
159
160    PROFILER_LOG_INFO(LOG_CORE, "HidumpPlugin: Loop exit");
161}
162
163template <typename T>
164bool HidumpPlugin::ParseHidumpInfo(T& hidumpInfoProto, char *buf, size_t len)
165{
166    UNUSED_PARAMETER(len);
167    // format: fps:123|1501960484673
168    if (strncmp(buf, "fps:", strlen("fps:")) != 0 && strncmp(buf, "sectionsFps:", strlen("sectionsFps:")) != 0) {
169        if (strstr(buf, "inaccessible or not found") != nullptr) {
170            PROFILER_LOG_ERROR(LOG_CORE, "HidumpPlugin: fps command not found!");
171        } else {
172            PROFILER_LOG_ERROR(LOG_CORE, "format error. %s", buf);
173        }
174        return false;
175    }
176
177    if (strncmp(buf, "fps:", strlen("fps:")) == 0) {
178        buf += strlen("fps:");
179    } else if (strncmp(buf, "sectionsFps:", strlen("sectionsFps:")) == 0) {
180        buf += strlen("sectionsFps:");
181    }
182
183    char *tmp = strchr(buf, '|');
184    CHECK_NOTNULL(tmp, false, "format error. %s", buf);
185    *tmp = ' ';
186    std::stringstream strvalue(buf);
187    uint32_t fps = 0;
188    strvalue >> fps;
189    uint64_t time_ms;
190    strvalue >> time_ms;
191
192    auto* eve = hidumpInfoProto.add_fps_event();
193    eve->set_fps(fps);
194    eve->set_id(::FpsData::REALTIME);
195    auto* time = eve->mutable_time();
196    time->set_tv_sec(time_ms / MS_PER_S);
197    time->set_tv_nsec((time_ms % MS_PER_S) * US_PER_S);
198
199    return true;
200}
201
202void HidumpPlugin::SetConfig(HidumpConfig& config)
203{
204    protoConfig_ = config;
205}
206
207int HidumpPlugin::SetTestCmd(const char *test_cmd)
208{
209    CHECK_NOTNULL(test_cmd, -1, "HidumpPlugin:%s test_cmd is null", __func__);
210    testCmd_ = const_cast<char *>(test_cmd);
211    return 0;
212}
213
214const char *HidumpPlugin::GetTestCmd(void)
215{
216    return testCmd_;
217}
218