1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
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 "lperf_events.h"
17
18 #include <chrono>
19 #include <fcntl.h>
20 #include <iostream>
21 #include <sys/ioctl.h>
22 #include <sys/mman.h>
23 #include <unistd.h>
24 #include "dfx_lperf.h"
25
26 namespace OHOS {
27 namespace HiviewDFX {
28 namespace {
29 #undef LOG_DOMAIN
30 #undef LOG_TAG
31 #define LOG_DOMAIN 0xD002D11
32 #define LOG_TAG "DfxLperfEvents"
33
34 const char *const LPERF_DEV = "/dev/lperf";
35 constexpr unsigned long LPERF_IOCTL_INIT = 1075866625;
36 constexpr unsigned long LPERF_IOCTL_PROFILE = 2148035586;
37 constexpr unsigned long LPERF_IOCTL_ADD_THREADS = 1074031620;
38 constexpr unsigned int DEFAULT_WATER_MARK = 5000;
39
40 #define HIPERF_BUF_ALIGN alignas(64)
41
42 struct LperfInitArg {
43 unsigned int rbSizeIntKb;
44 unsigned int samplePeriod;
45 unsigned int sampleInterval;
46 unsigned int watermark;
47 unsigned int reserved1;
48 unsigned long long rbAddr;
49 };
50
51 struct LperfThreadInputArg {
52 unsigned int tidCount;
53 unsigned int tids[MAX_SAMPLE_TIDS];
54 };
55 }
56
LperfEvents()57 LperfEvents::LperfEvents()
58 {
59 pageSize_ = static_cast<unsigned int>(sysconf(_SC_PAGESIZE));
60 }
61
~LperfEvents()62 LperfEvents::~LperfEvents()
63 {
64 Clear();
65 }
66
PrepareRecord()67 int LperfEvents::PrepareRecord()
68 {
69 CHECK_TRUE_AND_RET(PrepareFdEvents(), -1, "PrepareFdEvents failed");
70 CHECK_TRUE_AND_RET(AddRecordThreads(), -1, "AddRecordThreads failed");
71 return 0;
72 }
73
StartRecord()74 int LperfEvents::StartRecord()
75 {
76 CHECK_TRUE_AND_RET(PerfEventsEnable(true), -1, "LperfEvents EnableTracking() failed");
77 CHECK_TRUE_AND_RET(RecordLoop(), -1, "LperfEvents RecordLoop() failed");
78 CHECK_TRUE_AND_RET(PerfEventsEnable(false), -1, "LperfEvents RecordLoop() failed");
79 ReadRecordsFromMmaps();
80 return 0;
81 }
82
StopRecord()83 bool LperfEvents::StopRecord()
84 {
85 CHECK_TRUE_AND_RET(PerfEventsEnable(false), -1, "LperfEvents StopRecord failed");
86 return true;
87 }
88
SetTid(const std::vector<int> & tids)89 void LperfEvents::SetTid(const std::vector<int>& tids)
90 {
91 tids_ = tids;
92 }
93
SetTimeOut(int timeOut)94 void LperfEvents::SetTimeOut(int timeOut)
95 {
96 if (timeOut > 0) {
97 timeOut_ = timeOut;
98 }
99 }
100
SetSampleFrequency(unsigned int frequency)101 void LperfEvents::SetSampleFrequency(unsigned int frequency)
102 {
103 if (frequency > 0) {
104 sampleFreq_ = frequency;
105 }
106 }
107
PerfEventsEnable(bool enable)108 bool LperfEvents::PerfEventsEnable(bool enable)
109 {
110 int err = ioctl(lperfFd_, static_cast<unsigned long>(LPERF_IOCTL_PROFILE), enable ? 1 : 0);
111 CHECK_ERR(err, "enable lperfFd_ failed");
112 return true;
113 }
114
SetRecordCallBack(ProcessRecordCB recordCallBack)115 void LperfEvents::SetRecordCallBack(ProcessRecordCB recordCallBack)
116 {
117 recordCallBack_ = recordCallBack;
118 }
119
PrepareFdEvents()120 bool LperfEvents::PrepareFdEvents()
121 {
122 constexpr unsigned int sizekiB = 1024;
123 struct LperfInitArg initArg = {
124 .rbSizeIntKb = (mmapPages_ + 1) * pageSize_ / sizekiB,
125 .samplePeriod = sampleFreq_,
126 .sampleInterval = timeOut_,
127 .watermark = DEFAULT_WATER_MARK,
128 .rbAddr = 0,
129 };
130 lperfFd_ = open(LPERF_DEV, O_RDWR);
131 CHECK_ERR(lperfFd_, "open lperfFd_ failed");
132 int err = ioctl(lperfFd_, LPERF_IOCTL_INIT, &initArg);
133 CHECK_ERR(err, "init lperf failed");
134 lperfMmap_.fd = lperfFd_;
135 lperfMmap_.mmapPage = reinterpret_cast<perf_event_mmap_page *>(initArg.rbAddr);
136 lperfMmap_.buf = reinterpret_cast<uint8_t *>(initArg.rbAddr) + pageSize_;
137 lperfMmap_.bufSize = mmapPages_ * pageSize_;
138 pollFds_.emplace_back(pollfd {lperfFd_, POLLIN | POLLHUP, 0});
139 return true;
140 }
141
AddRecordThreads()142 bool LperfEvents::AddRecordThreads()
143 {
144 struct LperfThreadInputArg threadInfo;
145 size_t maxCount = 10;
146 size_t count = std::min(tids_.size(), maxCount);
147 threadInfo.tidCount = static_cast<unsigned int>(count);
148 for (size_t i = 0; i < count; i++) {
149 threadInfo.tids[i] = static_cast<unsigned int>(tids_[i]);
150 }
151 int err = ioctl(lperfFd_, static_cast<unsigned long>(LPERF_IOCTL_ADD_THREADS), &threadInfo);
152 CHECK_ERR(err, "add lperf threads failed");
153 return true;
154 }
155
GetHeaderFromMmap(MmapFd & mmap)156 bool LperfEvents::GetHeaderFromMmap(MmapFd& mmap)
157 {
158 if (mmap.dataSize <= 0) {
159 return false;
160 }
161
162 GetRecordFieldFromMmap(mmap, &(mmap.header), sizeof(mmap.header), mmap.mmapPage->data_tail, sizeof(mmap.header));
163 if (mmap.header.type != PERF_RECORD_SAMPLE) {
164 mmap.mmapPage->data_tail += mmap.header.size;
165 return false;
166 }
167 return true;
168 }
169
GetRecordFieldFromMmap(MmapFd & mmap,void * dest,size_t destSize,size_t pos,size_t size)170 void LperfEvents::GetRecordFieldFromMmap(MmapFd& mmap, void* dest, size_t destSize, size_t pos, size_t size)
171 {
172 if (mmap.bufSize == 0) {
173 return;
174 }
175 pos = pos % mmap.bufSize;
176 size_t tailSize = mmap.bufSize - pos;
177 size_t copySize = std::min(size, tailSize);
178 if (memcpy_s(dest, destSize, mmap.buf + pos, copySize) != 0) {
179 DFXLOGE("memcpy_s failed. size %{public}zd", copySize);
180 }
181 if (copySize < size && destSize > copySize) {
182 size -= copySize;
183 if (memcpy_s(static_cast<uint8_t *>(dest) + copySize, destSize - copySize, mmap.buf, size) != 0) {
184 DFXLOGE("memcpy_s mmap.buf to dest failed. size %{public}zd", size);
185 }
186 }
187 }
188
GetRecordFromMmap(MmapFd & mmap)189 void LperfEvents::GetRecordFromMmap(MmapFd& mmap)
190 {
191 HIPERF_BUF_ALIGN static uint8_t buf[RECORD_SIZE_LIMIT];
192 GetRecordFieldFromMmap(mmap, buf, RECORD_SIZE_LIMIT, mmap.mmapPage->data_tail, mmap.header.size);
193 __sync_synchronize();
194 mmap.mmapPage->data_tail += mmap.header.size;
195 mmap.dataSize -= mmap.header.size;
196 recordCallBack_(LperfRecordFactory::GetLperfRecord(mmap.header.type, buf));
197 }
198
ReadRecordsFromMmaps()199 void LperfEvents::ReadRecordsFromMmaps()
200 {
201 if (lperfMmap_.mmapPage->data_head <= lperfMmap_.mmapPage->data_tail) {
202 return;
203 }
204 size_t dataSize = static_cast<size_t>(lperfMmap_.mmapPage->data_head - lperfMmap_.mmapPage->data_tail);
205 __sync_synchronize();
206 lperfMmap_.dataSize = dataSize;
207 while (GetHeaderFromMmap(lperfMmap_)) {
208 GetRecordFromMmap(lperfMmap_);
209 }
210 }
211
RecordLoop()212 bool LperfEvents::RecordLoop()
213 {
214 CHECK_TRUE_AND_RET(pollFds_.size() > 0, false, "pollFds_ is invalid");
215 const auto endTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeOut_);
216
217 bool loopCondition = true;
218 int pollTimeout = 500;
219 while (loopCondition) {
220 const auto thisTime = std::chrono::steady_clock::now();
221 if (thisTime >= endTime) {
222 break;
223 }
224
225 if (poll(static_cast<struct pollfd *>(pollFds_.data()), pollFds_.size(), pollTimeout) <= 0) {
226 DFXLOGE("poll no data");
227 continue;
228 }
229 ReadRecordsFromMmaps();
230 if ((static_cast<unsigned short>(pollFds_[0].revents) & POLLHUP) == POLLHUP) {
231 DFXLOGE("poll status is POLLHUP, revents: %{public}d", pollFds_[0].revents);
232 loopCondition = false;
233 }
234 }
235 return true;
236 }
237
Clear()238 void LperfEvents::Clear()
239 {
240 LperfRecordFactory::ClearData();
241 pollFds_.clear();
242 if (lperfFd_ != -1) {
243 // munmap(lperfMmap_.mmapPage) in kernel when close lperfFd_.
244 close(lperfFd_);
245 lperfFd_ = -1;
246 }
247 lperfMmap_.mmapPage = nullptr;
248 }
249 } // namespace HiviewDFX
250 } // namespace OHOS
251