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