• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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