1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "los_perf_pri.h"
33 #include "perf_pmu_pri.h"
34 #include "perf_output_pri.h"
35 #include "los_init.h"
36 #include "los_process.h"
37 #include "los_tick.h"
38 #include "los_sys.h"
39 #include "los_spinlock.h"
40
41 STATIC Pmu *g_pmu = NULL;
42 STATIC PerfCB g_perfCb = {0};
43
44 LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_perfSpin);
45 #define PERF_LOCK(state) LOS_SpinLockSave(&g_perfSpin, &(state))
46 #define PERF_UNLOCK(state) LOS_SpinUnlockRestore(&g_perfSpin, (state))
47
48 #define MIN(x, y) ((x) < (y) ? (x) : (y))
49
OsPerfGetCurrTime(VOID)50 STATIC INLINE UINT64 OsPerfGetCurrTime(VOID)
51 {
52 #ifdef LOSCFG_PERF_CALC_TIME_BY_TICK
53 return LOS_TickCountGet();
54 #else
55 return HalClockGetCycles();
56 #endif
57 }
58
OsPmuInit(VOID)59 STATIC UINT32 OsPmuInit(VOID)
60 {
61 #ifdef LOSCFG_PERF_HW_PMU
62 if (OsHwPmuInit() != LOS_OK) {
63 return LOS_ERRNO_PERF_HW_INIT_ERROR;
64 }
65 #endif
66
67 #ifdef LOSCFG_PERF_TIMED_PMU
68 if (OsTimedPmuInit() != LOS_OK) {
69 return LOS_ERRNO_PERF_TIMED_INIT_ERROR;
70 }
71 #endif
72
73 #ifdef LOSCFG_PERF_SW_PMU
74 if (OsSwPmuInit() != LOS_OK) {
75 return LOS_ERRNO_PERF_SW_INIT_ERROR;
76 }
77 #endif
78 return LOS_OK;
79 }
80
OsPerfConfig(PerfEventConfig * eventsCfg)81 STATIC UINT32 OsPerfConfig(PerfEventConfig *eventsCfg)
82 {
83 UINT32 i;
84 UINT32 ret;
85
86 g_pmu = OsPerfPmuGet(eventsCfg->type);
87 if (g_pmu == NULL) {
88 PRINT_ERR("perf config type error %u!\n", eventsCfg->type);
89 return LOS_ERRNO_PERF_INVALID_PMU;
90 }
91
92 UINT32 eventNum = MIN(eventsCfg->eventsNr, PERF_MAX_EVENT);
93
94 (VOID)memset_s(&g_pmu->events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
95
96 for (i = 0; i < eventNum; i++) {
97 g_pmu->events.per[i].eventId = eventsCfg->events[i].eventId;
98 g_pmu->events.per[i].period = eventsCfg->events[i].period;
99 }
100 g_pmu->events.nr = i;
101 g_pmu->events.cntDivided = eventsCfg->predivided;
102 g_pmu->type = eventsCfg->type;
103
104 ret = g_pmu->config();
105 if (ret != LOS_OK) {
106 PRINT_ERR("perf config failed!\n");
107 (VOID)memset_s(&g_pmu->events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
108 return LOS_ERRNO_PERF_PMU_CONFIG_ERROR;
109 }
110 return LOS_OK;
111 }
112
OsPerfPrintCount(VOID)113 STATIC VOID OsPerfPrintCount(VOID)
114 {
115 UINT32 index;
116 UINT32 intSave;
117 UINT32 cpuid = ArchCurrCpuid();
118
119 PerfEvent *events = &g_pmu->events;
120 UINT32 eventNum = events->nr;
121
122 PERF_LOCK(intSave);
123 for (index = 0; index < eventNum; index++) {
124 Event *event = &(events->per[index]);
125
126 /* filter out event counter with no event binded. */
127 if (event->period == 0) {
128 continue;
129 }
130 PRINT_EMG("[%s] eventType: 0x%x [core %u]: %llu\n", g_pmu->getName(event), event->eventId, cpuid,
131 event->count[cpuid]);
132 }
133 PERF_UNLOCK(intSave);
134 }
135
OsPerfPrintTs(VOID)136 STATIC INLINE VOID OsPerfPrintTs(VOID)
137 {
138 #ifdef LOSCFG_PERF_CALC_TIME_BY_TICK
139 DOUBLE time = (g_perfCb.endTime - g_perfCb.startTime) * 1.0 / LOSCFG_BASE_CORE_TICK_PER_SECOND;
140 #else
141 DOUBLE time = (g_perfCb.endTime - g_perfCb.startTime) * 1.0 / OS_SYS_CLOCK;
142 #endif
143 PRINT_EMG("time used: %.6f(s)\n", time);
144 }
145
OsPerfStart(VOID)146 STATIC VOID OsPerfStart(VOID)
147 {
148 UINT32 cpuid = ArchCurrCpuid();
149
150 if (g_pmu == NULL) {
151 PRINT_ERR("pmu not registered!\n");
152 return;
153 }
154
155 if (g_perfCb.pmuStatusPerCpu[cpuid] != PERF_PMU_STARTED) {
156 UINT32 ret = g_pmu->start();
157 if (ret != LOS_OK) {
158 PRINT_ERR("perf start on core:%u failed, ret = 0x%x\n", cpuid, ret);
159 return;
160 }
161
162 g_perfCb.pmuStatusPerCpu[cpuid] = PERF_PMU_STARTED;
163 } else {
164 PRINT_ERR("percpu status err %d\n", g_perfCb.pmuStatusPerCpu[cpuid]);
165 }
166 }
167
OsPerfStop(VOID)168 STATIC VOID OsPerfStop(VOID)
169 {
170 UINT32 cpuid = ArchCurrCpuid();
171
172 if (g_pmu == NULL) {
173 PRINT_ERR("pmu not registered!\n");
174 return;
175 }
176
177 if (g_perfCb.pmuStatusPerCpu[cpuid] != PERF_PMU_STOPED) {
178 UINT32 ret = g_pmu->stop();
179 if (ret != LOS_OK) {
180 PRINT_ERR("perf stop on core:%u failed, ret = 0x%x\n", cpuid, ret);
181 return;
182 }
183
184 if (!g_perfCb.needSample) {
185 OsPerfPrintCount();
186 }
187
188 g_perfCb.pmuStatusPerCpu[cpuid] = PERF_PMU_STOPED;
189 } else {
190 PRINT_ERR("percpu status err %d\n", g_perfCb.pmuStatusPerCpu[cpuid]);
191 }
192 }
193
OsPerfSaveIpInfo(CHAR * buf,IpInfo * info)194 STATIC INLINE UINT32 OsPerfSaveIpInfo(CHAR *buf, IpInfo *info)
195 {
196 UINT32 size = 0;
197 #ifdef LOSCFG_KERNEL_VM
198 UINT32 len = ALIGN(info->len, sizeof(size_t));
199
200 *(UINTPTR *)buf = info->ip; /* save ip */
201 size += sizeof(UINTPTR);
202
203 *(UINT32 *)(buf + size) = len; /* save f_path length */
204 size += sizeof(UINT32);
205
206 if (strncpy_s(buf + size, REGION_PATH_MAX, info->f_path, info->len) != EOK) { /* save f_path */
207 PRINT_ERR("copy f_path failed, %s\n", info->f_path);
208 }
209 size += len;
210 #else
211 *(UINTPTR *)buf = info->ip; /* save ip */
212 size += sizeof(UINTPTR);
213 #endif
214 return size;
215 }
216
OsPerfBackTrace(PerfBackTrace * callChain,UINT32 maxDepth,PerfRegs * regs)217 STATIC UINT32 OsPerfBackTrace(PerfBackTrace *callChain, UINT32 maxDepth, PerfRegs *regs)
218 {
219 UINT32 count = BackTraceGet(regs->fp, (IpInfo *)(callChain->ip), maxDepth);
220 PRINT_DEBUG("backtrace depth = %u, fp = 0x%x\n", count, regs->fp);
221 return count;
222 }
223
OsPerfSaveBackTrace(CHAR * buf,PerfBackTrace * callChain,UINT32 count)224 STATIC INLINE UINT32 OsPerfSaveBackTrace(CHAR *buf, PerfBackTrace *callChain, UINT32 count)
225 {
226 UINT32 i;
227 *(UINT32 *)buf = count;
228 UINT32 size = sizeof(UINT32);
229 for (i = 0; i < count; i++) {
230 size += OsPerfSaveIpInfo(buf + size, &(callChain->ip[i]));
231 }
232 return size;
233 }
234
OsPerfCollectData(Event * event,PerfSampleData * data,PerfRegs * regs)235 STATIC UINT32 OsPerfCollectData(Event *event, PerfSampleData *data, PerfRegs *regs)
236 {
237 UINT32 size = 0;
238 UINT32 depth;
239 IpInfo pc = {0};
240 PerfBackTrace callChain = {0};
241 UINT32 sampleType = g_perfCb.sampleType;
242 CHAR *p = (CHAR *)data;
243
244 if (sampleType & PERF_RECORD_CPU) {
245 *(UINT32 *)(p + size) = ArchCurrCpuid();
246 size += sizeof(data->cpuid);
247 }
248
249 if (sampleType & PERF_RECORD_TID) {
250 *(UINT32 *)(p + size) = LOS_CurTaskIDGet();
251 size += sizeof(data->taskId);
252 }
253
254 if (sampleType & PERF_RECORD_PID) {
255 *(UINT32 *)(p + size) = LOS_GetCurrProcessID();
256 size += sizeof(data->processId);
257 }
258
259 if (sampleType & PERF_RECORD_TYPE) {
260 *(UINT32 *)(p + size) = event->eventId;
261 size += sizeof(data->eventId);
262 }
263
264 if (sampleType & PERF_RECORD_PERIOD) {
265 *(UINT32 *)(p + size) = event->period;
266 size += sizeof(data->period);
267 }
268
269 if (sampleType & PERF_RECORD_TIMESTAMP) {
270 *(UINT64 *)(p + size) = OsPerfGetCurrTime();
271 size += sizeof(data->time);
272 }
273
274 if (sampleType & PERF_RECORD_IP) {
275 OsGetUsrIpInfo(regs->pc, &pc);
276 size += OsPerfSaveIpInfo(p + size, &pc);
277 }
278
279 if (sampleType & PERF_RECORD_CALLCHAIN) {
280 depth = OsPerfBackTrace(&callChain, PERF_MAX_CALLCHAIN_DEPTH, regs);
281 size += OsPerfSaveBackTrace(p + size, &callChain, depth);
282 }
283
284 return size;
285 }
286
287 /*
288 * return TRUE if the taskId in the task filter list, return FALSE otherwise;
289 * return TRUE if user haven't specified any taskId(which is supposed
290 * to instrument the whole system)
291 */
OsFilterId(UINT32 id,const UINT32 * ids,UINT8 idsNr)292 STATIC INLINE BOOL OsFilterId(UINT32 id, const UINT32 *ids, UINT8 idsNr)
293 {
294 UINT32 i;
295 if (!idsNr) {
296 return TRUE;
297 }
298
299 for (i = 0; i < idsNr; i++) {
300 if (ids[i] == id) {
301 return TRUE;
302 }
303 }
304 return FALSE;
305 }
306
OsPerfFilter(UINT32 taskId,UINT32 processId)307 STATIC INLINE BOOL OsPerfFilter(UINT32 taskId, UINT32 processId)
308 {
309 return OsFilterId(taskId, g_perfCb.taskIds, g_perfCb.taskIdsNr) &&
310 OsFilterId(processId, g_perfCb.processIds, g_perfCb.processIdsNr);
311 }
312
OsPerfParamValid(VOID)313 STATIC INLINE UINT32 OsPerfParamValid(VOID)
314 {
315 UINT32 index;
316 UINT32 res = 0;
317
318 if (g_pmu == NULL) {
319 return 0;
320 }
321 PerfEvent *events = &g_pmu->events;
322 UINT32 eventNum = events->nr;
323
324 for (index = 0; index < eventNum; index++) {
325 res |= events->per[index].period;
326 }
327 return res;
328 }
329
OsPerfHdrInit(UINT32 id)330 STATIC UINT32 OsPerfHdrInit(UINT32 id)
331 {
332 PerfDataHdr head = {
333 .magic = PERF_DATA_MAGIC_WORD,
334 .sampleType = g_perfCb.sampleType,
335 .sectionId = id,
336 .eventType = g_pmu->type,
337 .len = sizeof(PerfDataHdr),
338 };
339 return OsPerfOutputWrite((CHAR *)&head, head.len);
340 }
341
OsPerfUpdateEventCount(Event * event,UINT32 value)342 VOID OsPerfUpdateEventCount(Event *event, UINT32 value)
343 {
344 if (event == NULL) {
345 return;
346 }
347 event->count[ArchCurrCpuid()] += (value & 0xFFFFFFFF); /* event->count is UINT64 */
348 }
349
OsPerfHandleOverFlow(Event * event,PerfRegs * regs)350 VOID OsPerfHandleOverFlow(Event *event, PerfRegs *regs)
351 {
352 PerfSampleData data;
353 UINT32 len;
354
355 (VOID)memset_s(&data, sizeof(PerfSampleData), 0, sizeof(PerfSampleData));
356 if ((g_perfCb.needSample) && OsPerfFilter(LOS_CurTaskIDGet(), LOS_GetCurrProcessID())) {
357 len = OsPerfCollectData(event, &data, regs);
358 OsPerfOutputWrite((CHAR *)&data, len);
359 }
360 }
361
OsPerfInit(VOID)362 STATIC UINT32 OsPerfInit(VOID)
363 {
364 UINT32 ret;
365 if (g_perfCb.status != PERF_UNINIT) {
366 ret = LOS_ERRNO_PERF_STATUS_INVALID;
367 goto PERF_INIT_ERROR;
368 }
369
370 ret = OsPmuInit();
371 if (ret != LOS_OK) {
372 goto PERF_INIT_ERROR;
373 }
374
375 ret = OsPerfOutputInit(NULL, LOSCFG_PERF_BUFFER_SIZE);
376 if (ret != LOS_OK) {
377 ret = LOS_ERRNO_PERF_BUF_ERROR;
378 goto PERF_INIT_ERROR;
379 }
380 g_perfCb.status = PERF_STOPPED;
381 PERF_INIT_ERROR:
382 return ret;
383 }
384
PerfInfoDump(VOID)385 STATIC VOID PerfInfoDump(VOID)
386 {
387 UINT32 i;
388 if (g_pmu != NULL) {
389 PRINTK("type: %d\n", g_pmu->type);
390 for (i = 0; i < g_pmu->events.nr; i++) {
391 PRINTK("events[%d]: %d, 0x%x\n", i, g_pmu->events.per[i].eventId, g_pmu->events.per[i].period);
392 }
393 PRINTK("predivided: %d\n", g_pmu->events.cntDivided);
394 } else {
395 PRINTK("pmu is NULL\n");
396 }
397
398 PRINTK("sampleType: 0x%x\n", g_perfCb.sampleType);
399 for (i = 0; i < g_perfCb.taskIdsNr; i++) {
400 PRINTK("filter taskIds[%d]: %d\n", i, g_perfCb.taskIds[i]);
401 }
402 for (i = 0; i < g_perfCb.processIdsNr; i++) {
403 PRINTK("filter processIds[%d]: %d\n", i, g_perfCb.processIds[i]);
404 }
405 PRINTK("needSample: %d\n", g_perfCb.needSample);
406 }
407
OsPerfSetFilterIds(UINT32 * dstIds,UINT8 * dstIdsNr,UINT32 * ids,UINT32 idsNr)408 STATIC INLINE VOID OsPerfSetFilterIds(UINT32 *dstIds, UINT8 *dstIdsNr, UINT32 *ids, UINT32 idsNr)
409 {
410 errno_t ret;
411 if (idsNr) {
412 ret = memcpy_s(dstIds, PERF_MAX_FILTER_TSKS * sizeof(UINT32), ids, idsNr * sizeof(UINT32));
413 if (ret != EOK) {
414 PRINT_ERR("In %s At line:%d execute memcpy_s error\n", __FUNCTION__, __LINE__);
415 *dstIdsNr = 0;
416 return;
417 }
418 *dstIdsNr = MIN(idsNr, PERF_MAX_FILTER_TSKS);
419 } else {
420 *dstIdsNr = 0;
421 }
422 }
423
LOS_PerfConfig(PerfConfigAttr * attr)424 UINT32 LOS_PerfConfig(PerfConfigAttr *attr)
425 {
426 UINT32 ret;
427 UINT32 intSave;
428
429 if (attr == NULL) {
430 return LOS_ERRNO_PERF_CONFIG_NULL;
431 }
432
433 PERF_LOCK(intSave);
434 if (g_perfCb.status != PERF_STOPPED) {
435 ret = LOS_ERRNO_PERF_STATUS_INVALID;
436 PRINT_ERR("perf config status error : 0x%x\n", g_perfCb.status);
437 goto PERF_CONFIG_ERROR;
438 }
439
440 g_pmu = NULL;
441
442 g_perfCb.needSample = attr->needSample;
443 g_perfCb.sampleType = attr->sampleType;
444
445 OsPerfSetFilterIds(g_perfCb.taskIds, &g_perfCb.taskIdsNr, attr->taskIds, attr->taskIdsNr);
446 OsPerfSetFilterIds(g_perfCb.processIds, &g_perfCb.processIdsNr, attr->processIds, attr->processIdsNr);
447
448 ret = OsPerfConfig(&attr->eventsCfg);
449 PerfInfoDump();
450 PERF_CONFIG_ERROR:
451 PERF_UNLOCK(intSave);
452 return ret;
453 }
454
LOS_PerfStart(UINT32 sectionId)455 VOID LOS_PerfStart(UINT32 sectionId)
456 {
457 UINT32 intSave;
458 UINT32 ret;
459
460 PERF_LOCK(intSave);
461 if (g_perfCb.status != PERF_STOPPED) {
462 PRINT_ERR("perf start status error : 0x%x\n", g_perfCb.status);
463 goto PERF_START_ERROR;
464 }
465
466 if (!OsPerfParamValid()) {
467 PRINT_ERR("forgot call `LOS_PerfConfig(...)` before perf start?\n");
468 goto PERF_START_ERROR;
469 }
470
471 if (g_perfCb.needSample) {
472 ret = OsPerfHdrInit(sectionId); /* section header init */
473 if (ret != LOS_OK) {
474 PRINT_ERR("perf hdr init error 0x%x\n", ret);
475 goto PERF_START_ERROR;
476 }
477 }
478
479 SMP_CALL_PERF_FUNC(OsPerfStart); /* send to all cpu to start pmu */
480 g_perfCb.status = PERF_STARTED;
481 g_perfCb.startTime = OsPerfGetCurrTime();
482 PERF_START_ERROR:
483 PERF_UNLOCK(intSave);
484 return;
485 }
486
LOS_PerfStop(VOID)487 VOID LOS_PerfStop(VOID)
488 {
489 UINT32 intSave;
490
491 PERF_LOCK(intSave);
492 if (g_perfCb.status != PERF_STARTED) {
493 PRINT_ERR("perf stop status error : 0x%x\n", g_perfCb.status);
494 goto PERF_STOP_ERROR;
495 }
496
497 SMP_CALL_PERF_FUNC(OsPerfStop); /* send to all cpu to stop pmu */
498
499 OsPerfOutputFlush();
500
501 if (g_perfCb.needSample) {
502 OsPerfOutputInfo();
503 }
504
505 g_perfCb.status = PERF_STOPPED;
506 g_perfCb.endTime = OsPerfGetCurrTime();
507
508 OsPerfPrintTs();
509 PERF_STOP_ERROR:
510 PERF_UNLOCK(intSave);
511 return;
512 }
513
LOS_PerfDataRead(CHAR * dest,UINT32 size)514 UINT32 LOS_PerfDataRead(CHAR *dest, UINT32 size)
515 {
516 return OsPerfOutputRead(dest, size);
517 }
518
LOS_PerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func)519 VOID LOS_PerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func)
520 {
521 UINT32 intSave;
522
523 PERF_LOCK(intSave);
524 OsPerfNotifyHookReg(func);
525 PERF_UNLOCK(intSave);
526 }
527
LOS_PerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func)528 VOID LOS_PerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func)
529 {
530 UINT32 intSave;
531
532 PERF_LOCK(intSave);
533 OsPerfFlushHookReg(func);
534 PERF_UNLOCK(intSave);
535 }
536
OsPerfSetIrqRegs(UINTPTR pc,UINTPTR fp)537 VOID OsPerfSetIrqRegs(UINTPTR pc, UINTPTR fp)
538 {
539 LosTaskCB *runTask = (LosTaskCB *)ArchCurrTaskGet();
540 runTask->pc = pc;
541 runTask->fp = fp;
542 }
543
544 LOS_MODULE_INIT(OsPerfInit, LOS_INIT_LEVEL_KMOD_EXTENDED);
545