1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2023. 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
16 #include "gpu_data_plugin.h"
17 #include <ctime>
18 #include <numeric>
19 #include "common.h"
20 #include "gpu_plugin_result.pbencoder.h"
21
22 namespace {
23 using namespace OHOS::Developtools::Profiler;
24 const std::string GPU_PATH = "/sys/class/devfreq/gpufreq/gpu_scene_aware/utilisation";
25 constexpr uint32_t RET_COUNT = 50;
26 constexpr uint32_t SAMPLE_INTERVAL = 10;
27 constexpr uint32_t SAMPLE_INTERVAL_COMPATIBLE = 500;
28 } // namespace
29
Start(const uint8_t * configData,uint32_t configSize)30 int GpuDataPlugin::Start(const uint8_t* configData, uint32_t configSize)
31 {
32 CHECK_TRUE(protoConfig_.ParseFromArray(configData, configSize) > 0, RET_FAIL,
33 "%s:parseFromArray failed!", __func__);
34
35 if (protoConfig_.pid() > 0) {
36 pid_ = protoConfig_.pid();
37 }
38
39 file_.open(GPU_PATH);
40 auto args = GetCmdArgs(protoConfig_);
41 if (!file_.is_open()) {
42 int ret = COMMON::PluginWriteToHisysevent("gpu_plugin", "sh", args, RET_FAIL, "failed");
43 PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to open(%s), hisysevent report gpu_plugin ret: %d",
44 __func__, GPU_PATH.c_str(), ret);
45 return RET_FAIL;
46 }
47 CHECK_NOTNULL(resultWriter_, -1, "GPUDataPlugin: Writer is no set!");
48 CHECK_NOTNULL(resultWriter_->write, -1, "GPUDataPlugin: Writer.write is no set!");
49 CHECK_NOTNULL(resultWriter_->flush, -1, "GPUDataPlugin: Writer.flush is no set!");
50 running_ = true;
51 if (protoConfig_.report_gpu_data_array()) {
52 writeThread_ = std::thread([this] { this->ReadGpuDataArray(); });
53 } else {
54 writeThread_ = std::thread([this] { this->ReadGpuData(); });
55 }
56 int ret = COMMON::PluginWriteToHisysevent("gpu_plugin", "sh", args, RET_SUCC, "success");
57 PROFILER_LOG_INFO(LOG_CORE, "%s:start success! hisysevent report gpu_plugin result: %d", __func__, ret);
58 return RET_SUCC;
59 }
60
GetCmdArgs(const GpuConfig & traceConfig)61 std::string GpuDataPlugin::GetCmdArgs(const GpuConfig& traceConfig)
62 {
63 std::string args;
64 args += "pid: " + std::to_string(traceConfig.pid()) + ", report_gpu_info: ";
65 args += (traceConfig.report_gpu_info() ? "true" : "false");
66 return args;
67 }
68
ReadGpuDataArray()69 void GpuDataPlugin::ReadGpuDataArray()
70 {
71 PROFILER_LOG_INFO(LOG_CORE, "GPUDataPlugin: Read GPU data start");
72 CHECK_NOTNULL(resultWriter_, NO_RETVAL, "%s: resultWriter_ is nullptr", __func__);
73 std::vector<std::pair<uint64_t, int>> vectGpuData = {};
74 uint32_t nCount = 0;
75 while (running_) {
76 int retGpu = ReadFile();
77 if (retGpu != RET_FAIL) {
78 uint64_t bootTime = GetBootTime();
79 vectGpuData.emplace_back(std::make_pair(bootTime, retGpu));
80 nCount++;
81 }
82 if (nCount == RET_COUNT) {
83 FlushGpuData(vectGpuData);
84 nCount = 0;
85 vectGpuData.clear();
86 }
87 std::this_thread::sleep_for(std::chrono::milliseconds(SAMPLE_INTERVAL));
88 }
89 if (vectGpuData.size() > 0) {
90 FlushGpuData(vectGpuData);
91 }
92 PROFILER_LOG_INFO(LOG_CORE, "GPUDataPlugin: Read data end");
93 }
94
ReadGpuData()95 void GpuDataPlugin::ReadGpuData()
96 {
97 PROFILER_LOG_INFO(LOG_CORE, "GPUDataPlugin: Read GPU data start");
98 CHECK_NOTNULL(resultWriter_, NO_RETVAL, "%s: resultWriter_ is nullptr", __func__);
99 while (running_) {
100 if (resultWriter_->isProtobufSerialize) {
101 GpuData dataProto;
102 WriteGpuDataInfo(dataProto);
103 if (dataProto.ByteSizeLong() > 0) {
104 buffer_.resize(dataProto.ByteSizeLong());
105 dataProto.SerializeToArray(buffer_.data(), buffer_.size());
106 resultWriter_->write(resultWriter_, buffer_.data(), buffer_.size());
107 resultWriter_->flush(resultWriter_);
108 }
109 } else {
110 ProtoEncoder::GpuData dataProto(resultWriter_->startReport(resultWriter_));
111 WriteGpuDataInfo(dataProto);
112 int messageLen = dataProto.Finish();
113 resultWriter_->finishReport(resultWriter_, messageLen);
114 resultWriter_->flush(resultWriter_);
115 }
116 std::this_thread::sleep_for(std::chrono::milliseconds(SAMPLE_INTERVAL_COMPATIBLE));
117 }
118 PROFILER_LOG_INFO(LOG_CORE, "GPUDataPlugin: Read data end");
119 }
120
FlushGpuData(std::vector<std::pair<uint64_t,int>> & vectGpuData)121 void GpuDataPlugin::FlushGpuData(std::vector<std::pair<uint64_t, int>>& vectGpuData)
122 {
123 if (resultWriter_->isProtobufSerialize) {
124 GpuData dataProto;
125 WriteGpuDataInfoExt(dataProto, vectGpuData);
126 if (dataProto.ByteSizeLong() > 0) {
127 buffer_.resize(dataProto.ByteSizeLong());
128 dataProto.SerializeToArray(buffer_.data(), buffer_.size());
129 resultWriter_->write(resultWriter_, buffer_.data(), buffer_.size());
130 resultWriter_->flush(resultWriter_);
131 }
132 } else {
133 ProtoEncoder::GpuData dataProto(resultWriter_->startReport(resultWriter_));
134 WriteGpuDataInfoExt(dataProto, vectGpuData);
135 int messageLen = dataProto.Finish();
136 resultWriter_->finishReport(resultWriter_, messageLen);
137 resultWriter_->flush(resultWriter_);
138 }
139 }
140
Stop()141 int GpuDataPlugin::Stop()
142 {
143 running_ = false;
144 if (writeThread_.joinable()) {
145 writeThread_.join();
146 }
147 PROFILER_LOG_INFO(LOG_CORE, "GPUDataPlugin:stop thread success!");
148 file_.close();
149 PROFILER_LOG_INFO(LOG_CORE, "GPUDataPlugin: stop success!");
150 return 0;
151 }
152
ReadFile()153 int GpuDataPlugin::ReadFile()
154 {
155 file_.clear();
156 file_.seekg(0);
157 std::string line;
158 std::getline(file_, line);
159 if (line == "") {
160 return RET_FAIL;
161 }
162 for (char charac : line) {
163 if (!isdigit(charac)) {
164 PROFILER_LOG_ERROR(LOG_CORE, "invalid file content for (%s)", GPU_PATH.c_str());
165 return RET_FAIL;
166 }
167 }
168 return stoi(line);
169 }
170
GetBootTime()171 uint64_t GpuDataPlugin::GetBootTime()
172 {
173 constexpr uint64_t nanoSeconds = 1000000000;
174 struct timespec ts;
175 int result = clock_gettime(CLOCK_BOOTTIME, &ts);
176 if (result == -1) {
177 PROFILER_LOG_ERROR(LOG_CORE, "clock_gettime failed");
178 return 0;
179 }
180 uint64_t bootTime = (static_cast<uint64_t>(ts.tv_sec) * nanoSeconds + static_cast<uint64_t>(ts.tv_nsec)) / 1000000;
181 return bootTime;
182 }
183
184 template <typename T>
WriteGpuDataInfoExt(T & gpuData,std::vector<std::pair<uint64_t,int>> & vectGpuData)185 void GpuDataPlugin::WriteGpuDataInfoExt(T& gpuData, std::vector<std::pair<uint64_t, int>>& vectGpuData)
186 {
187 gpuData.set_boottime(0);
188 gpuData.set_gpu_utilisation(0);
189 for (auto& gpuDataInfo : vectGpuData) {
190 auto* gpuDataExt = gpuData.add_gpu_data_array();
191 gpuDataExt->set_boottime(gpuDataInfo.first);
192 gpuDataExt->set_gpu_utilisation(gpuDataInfo.second);
193 }
194 }
195
196 template <typename T>
WriteGpuDataInfo(T & gpuData)197 void GpuDataPlugin::WriteGpuDataInfo(T& gpuData)
198 {
199 int ret = ReadFile();
200 if (ret == RET_FAIL) {
201 return;
202 }
203 constexpr uint64_t nanoSeconds = 1000000000;
204 struct timespec ts;
205 int result = clock_gettime(CLOCK_BOOTTIME, &ts);
206 if (result == -1) {
207 PROFILER_LOG_ERROR(LOG_CORE, "clock_gettime failed");
208 return;
209 }
210 uint64_t boottime = (static_cast<uint64_t>(ts.tv_sec) * nanoSeconds + static_cast<uint64_t>(ts.tv_nsec)) / 1000000;
211 gpuData.set_boottime(boottime);
212 gpuData.set_gpu_utilisation(static_cast<uint64_t>(ret));
213 }
214
SetWriter(WriterStruct * writer)215 int GpuDataPlugin::SetWriter(WriterStruct* writer)
216 {
217 resultWriter_ = writer;
218 return 0;
219 }
220