545 lines
15 KiB
C
545 lines
15 KiB
C
/*
|
|
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
|
|
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
* conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
* of conditions and the following disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
|
|
* to endorse or promote products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "los_perf_pri.h"
|
|
#include "perf_pmu_pri.h"
|
|
#include "perf_output_pri.h"
|
|
#include "los_init.h"
|
|
#include "los_process.h"
|
|
#include "los_tick.h"
|
|
#include "los_sys.h"
|
|
#include "los_spinlock.h"
|
|
|
|
STATIC Pmu *g_pmu = NULL;
|
|
STATIC PerfCB g_perfCb = {0};
|
|
|
|
LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_perfSpin);
|
|
#define PERF_LOCK(state) LOS_SpinLockSave(&g_perfSpin, &(state))
|
|
#define PERF_UNLOCK(state) LOS_SpinUnlockRestore(&g_perfSpin, (state))
|
|
|
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
STATIC INLINE UINT64 OsPerfGetCurrTime(VOID)
|
|
{
|
|
#ifdef LOSCFG_PERF_CALC_TIME_BY_TICK
|
|
return LOS_TickCountGet();
|
|
#else
|
|
return HalClockGetCycles();
|
|
#endif
|
|
}
|
|
|
|
STATIC UINT32 OsPmuInit(VOID)
|
|
{
|
|
#ifdef LOSCFG_PERF_HW_PMU
|
|
if (OsHwPmuInit() != LOS_OK) {
|
|
return LOS_ERRNO_PERF_HW_INIT_ERROR;
|
|
}
|
|
#endif
|
|
|
|
#ifdef LOSCFG_PERF_TIMED_PMU
|
|
if (OsTimedPmuInit() != LOS_OK) {
|
|
return LOS_ERRNO_PERF_TIMED_INIT_ERROR;
|
|
}
|
|
#endif
|
|
|
|
#ifdef LOSCFG_PERF_SW_PMU
|
|
if (OsSwPmuInit() != LOS_OK) {
|
|
return LOS_ERRNO_PERF_SW_INIT_ERROR;
|
|
}
|
|
#endif
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC UINT32 OsPerfConfig(PerfEventConfig *eventsCfg)
|
|
{
|
|
UINT32 i;
|
|
UINT32 ret;
|
|
|
|
g_pmu = OsPerfPmuGet(eventsCfg->type);
|
|
if (g_pmu == NULL) {
|
|
PRINT_ERR("perf config type error %u!\n", eventsCfg->type);
|
|
return LOS_ERRNO_PERF_INVALID_PMU;
|
|
}
|
|
|
|
UINT32 eventNum = MIN(eventsCfg->eventsNr, PERF_MAX_EVENT);
|
|
|
|
(VOID)memset_s(&g_pmu->events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
|
|
|
|
for (i = 0; i < eventNum; i++) {
|
|
g_pmu->events.per[i].eventId = eventsCfg->events[i].eventId;
|
|
g_pmu->events.per[i].period = eventsCfg->events[i].period;
|
|
}
|
|
g_pmu->events.nr = i;
|
|
g_pmu->events.cntDivided = eventsCfg->predivided;
|
|
g_pmu->type = eventsCfg->type;
|
|
|
|
ret = g_pmu->config();
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("perf config failed!\n");
|
|
(VOID)memset_s(&g_pmu->events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
|
|
return LOS_ERRNO_PERF_PMU_CONFIG_ERROR;
|
|
}
|
|
return LOS_OK;
|
|
}
|
|
|
|
STATIC VOID OsPerfPrintCount(VOID)
|
|
{
|
|
UINT32 index;
|
|
UINT32 intSave;
|
|
UINT32 cpuid = ArchCurrCpuid();
|
|
|
|
PerfEvent *events = &g_pmu->events;
|
|
UINT32 eventNum = events->nr;
|
|
|
|
PERF_LOCK(intSave);
|
|
for (index = 0; index < eventNum; index++) {
|
|
Event *event = &(events->per[index]);
|
|
|
|
/* filter out event counter with no event binded. */
|
|
if (event->period == 0) {
|
|
continue;
|
|
}
|
|
PRINT_EMG("[%s] eventType: 0x%x [core %u]: %llu\n", g_pmu->getName(event), event->eventId, cpuid,
|
|
event->count[cpuid]);
|
|
}
|
|
PERF_UNLOCK(intSave);
|
|
}
|
|
|
|
STATIC INLINE VOID OsPerfPrintTs(VOID)
|
|
{
|
|
#ifdef LOSCFG_PERF_CALC_TIME_BY_TICK
|
|
DOUBLE time = (g_perfCb.endTime - g_perfCb.startTime) * 1.0 / LOSCFG_BASE_CORE_TICK_PER_SECOND;
|
|
#else
|
|
DOUBLE time = (g_perfCb.endTime - g_perfCb.startTime) * 1.0 / OS_SYS_CLOCK;
|
|
#endif
|
|
PRINT_EMG("time used: %.6f(s)\n", time);
|
|
}
|
|
|
|
STATIC VOID OsPerfStart(VOID)
|
|
{
|
|
UINT32 cpuid = ArchCurrCpuid();
|
|
|
|
if (g_pmu == NULL) {
|
|
PRINT_ERR("pmu not registered!\n");
|
|
return;
|
|
}
|
|
|
|
if (g_perfCb.pmuStatusPerCpu[cpuid] != PERF_PMU_STARTED) {
|
|
UINT32 ret = g_pmu->start();
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("perf start on core:%u failed, ret = 0x%x\n", cpuid, ret);
|
|
return;
|
|
}
|
|
|
|
g_perfCb.pmuStatusPerCpu[cpuid] = PERF_PMU_STARTED;
|
|
} else {
|
|
PRINT_ERR("percpu status err %d\n", g_perfCb.pmuStatusPerCpu[cpuid]);
|
|
}
|
|
}
|
|
|
|
STATIC VOID OsPerfStop(VOID)
|
|
{
|
|
UINT32 cpuid = ArchCurrCpuid();
|
|
|
|
if (g_pmu == NULL) {
|
|
PRINT_ERR("pmu not registered!\n");
|
|
return;
|
|
}
|
|
|
|
if (g_perfCb.pmuStatusPerCpu[cpuid] != PERF_PMU_STOPED) {
|
|
UINT32 ret = g_pmu->stop();
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("perf stop on core:%u failed, ret = 0x%x\n", cpuid, ret);
|
|
return;
|
|
}
|
|
|
|
if (!g_perfCb.needSample) {
|
|
OsPerfPrintCount();
|
|
}
|
|
|
|
g_perfCb.pmuStatusPerCpu[cpuid] = PERF_PMU_STOPED;
|
|
} else {
|
|
PRINT_ERR("percpu status err %d\n", g_perfCb.pmuStatusPerCpu[cpuid]);
|
|
}
|
|
}
|
|
|
|
STATIC INLINE UINT32 OsPerfSaveIpInfo(CHAR *buf, IpInfo *info)
|
|
{
|
|
UINT32 size = 0;
|
|
#ifdef LOSCFG_KERNEL_VM
|
|
UINT32 len = ALIGN(info->len, sizeof(size_t));
|
|
|
|
*(UINTPTR *)buf = info->ip; /* save ip */
|
|
size += sizeof(UINTPTR);
|
|
|
|
*(UINT32 *)(buf + size) = len; /* save f_path length */
|
|
size += sizeof(UINT32);
|
|
|
|
if (strncpy_s(buf + size, REGION_PATH_MAX, info->f_path, info->len) != EOK) { /* save f_path */
|
|
PRINT_ERR("copy f_path failed, %s\n", info->f_path);
|
|
}
|
|
size += len;
|
|
#else
|
|
*(UINTPTR *)buf = info->ip; /* save ip */
|
|
size += sizeof(UINTPTR);
|
|
#endif
|
|
return size;
|
|
}
|
|
|
|
STATIC UINT32 OsPerfBackTrace(PerfBackTrace *callChain, UINT32 maxDepth, PerfRegs *regs)
|
|
{
|
|
UINT32 count = BackTraceGet(regs->fp, (IpInfo *)(callChain->ip), maxDepth);
|
|
PRINT_DEBUG("backtrace depth = %u, fp = 0x%x\n", count, regs->fp);
|
|
return count;
|
|
}
|
|
|
|
STATIC INLINE UINT32 OsPerfSaveBackTrace(CHAR *buf, PerfBackTrace *callChain, UINT32 count)
|
|
{
|
|
UINT32 i;
|
|
*(UINT32 *)buf = count;
|
|
UINT32 size = sizeof(UINT32);
|
|
for (i = 0; i < count; i++) {
|
|
size += OsPerfSaveIpInfo(buf + size, &(callChain->ip[i]));
|
|
}
|
|
return size;
|
|
}
|
|
|
|
STATIC UINT32 OsPerfCollectData(Event *event, PerfSampleData *data, PerfRegs *regs)
|
|
{
|
|
UINT32 size = 0;
|
|
UINT32 depth;
|
|
IpInfo pc = {0};
|
|
PerfBackTrace callChain = {0};
|
|
UINT32 sampleType = g_perfCb.sampleType;
|
|
CHAR *p = (CHAR *)data;
|
|
|
|
if (sampleType & PERF_RECORD_CPU) {
|
|
*(UINT32 *)(p + size) = ArchCurrCpuid();
|
|
size += sizeof(data->cpuid);
|
|
}
|
|
|
|
if (sampleType & PERF_RECORD_TID) {
|
|
*(UINT32 *)(p + size) = LOS_CurTaskIDGet();
|
|
size += sizeof(data->taskId);
|
|
}
|
|
|
|
if (sampleType & PERF_RECORD_PID) {
|
|
*(UINT32 *)(p + size) = LOS_GetCurrProcessID();
|
|
size += sizeof(data->processId);
|
|
}
|
|
|
|
if (sampleType & PERF_RECORD_TYPE) {
|
|
*(UINT32 *)(p + size) = event->eventId;
|
|
size += sizeof(data->eventId);
|
|
}
|
|
|
|
if (sampleType & PERF_RECORD_PERIOD) {
|
|
*(UINT32 *)(p + size) = event->period;
|
|
size += sizeof(data->period);
|
|
}
|
|
|
|
if (sampleType & PERF_RECORD_TIMESTAMP) {
|
|
*(UINT64 *)(p + size) = OsPerfGetCurrTime();
|
|
size += sizeof(data->time);
|
|
}
|
|
|
|
if (sampleType & PERF_RECORD_IP) {
|
|
OsGetUsrIpInfo(regs->pc, &pc);
|
|
size += OsPerfSaveIpInfo(p + size, &pc);
|
|
}
|
|
|
|
if (sampleType & PERF_RECORD_CALLCHAIN) {
|
|
depth = OsPerfBackTrace(&callChain, PERF_MAX_CALLCHAIN_DEPTH, regs);
|
|
size += OsPerfSaveBackTrace(p + size, &callChain, depth);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* return TRUE if the taskId in the task filter list, return FALSE otherwise;
|
|
* return TRUE if user haven't specified any taskId(which is supposed
|
|
* to instrument the whole system)
|
|
*/
|
|
STATIC INLINE BOOL OsFilterId(UINT32 id, UINT32 *ids, UINT8 idsNr)
|
|
{
|
|
UINT32 i;
|
|
if (!idsNr) {
|
|
return TRUE;
|
|
}
|
|
|
|
for (i = 0; i < idsNr; i++) {
|
|
if (ids[i] == id) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC INLINE BOOL OsPerfFilter(UINT32 taskId, UINT32 processId)
|
|
{
|
|
return OsFilterId(taskId, g_perfCb.taskIds, g_perfCb.taskIdsNr) &&
|
|
OsFilterId(processId, g_perfCb.processIds, g_perfCb.processIdsNr);
|
|
}
|
|
|
|
STATIC INLINE UINT32 OsPerfParamValid(VOID)
|
|
{
|
|
UINT32 index;
|
|
UINT32 res = 0;
|
|
|
|
if (g_pmu == NULL) {
|
|
return 0;
|
|
}
|
|
PerfEvent *events = &g_pmu->events;
|
|
UINT32 eventNum = events->nr;
|
|
|
|
for (index = 0; index < eventNum; index++) {
|
|
res |= events->per[index].period;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
STATIC UINT32 OsPerfHdrInit(UINT32 id)
|
|
{
|
|
PerfDataHdr head = {
|
|
.magic = PERF_DATA_MAGIC_WORD,
|
|
.sampleType = g_perfCb.sampleType,
|
|
.sectionId = id,
|
|
.eventType = g_pmu->type,
|
|
.len = sizeof(PerfDataHdr),
|
|
};
|
|
return OsPerfOutputWrite((CHAR *)&head, head.len);
|
|
}
|
|
|
|
VOID OsPerfUpdateEventCount(Event *event, UINT32 value)
|
|
{
|
|
if (event == NULL) {
|
|
return;
|
|
}
|
|
event->count[ArchCurrCpuid()] += (value & 0xFFFFFFFF); /* event->count is UINT64 */
|
|
}
|
|
|
|
VOID OsPerfHandleOverFlow(Event *event, PerfRegs *regs)
|
|
{
|
|
PerfSampleData data;
|
|
UINT32 len;
|
|
|
|
(VOID)memset_s(&data, sizeof(PerfSampleData), 0, sizeof(PerfSampleData));
|
|
if ((g_perfCb.needSample) && OsPerfFilter(LOS_CurTaskIDGet(), LOS_GetCurrProcessID())) {
|
|
len = OsPerfCollectData(event, &data, regs);
|
|
OsPerfOutputWrite((CHAR *)&data, len);
|
|
}
|
|
}
|
|
|
|
STATIC UINT32 OsPerfInit(VOID)
|
|
{
|
|
UINT32 ret;
|
|
if (g_perfCb.status != PERF_UNINIT) {
|
|
ret = LOS_ERRNO_PERF_STATUS_INVALID;
|
|
goto PERF_INIT_ERROR;
|
|
}
|
|
|
|
ret = OsPmuInit();
|
|
if (ret != LOS_OK) {
|
|
goto PERF_INIT_ERROR;
|
|
}
|
|
|
|
ret = OsPerfOutputInit(NULL, LOSCFG_PERF_BUFFER_SIZE);
|
|
if (ret != LOS_OK) {
|
|
ret = LOS_ERRNO_PERF_BUF_ERROR;
|
|
goto PERF_INIT_ERROR;
|
|
}
|
|
g_perfCb.status = PERF_STOPPED;
|
|
PERF_INIT_ERROR:
|
|
return ret;
|
|
}
|
|
|
|
STATIC VOID PerfInfoDump(VOID)
|
|
{
|
|
UINT32 i;
|
|
if (g_pmu != NULL) {
|
|
PRINTK("type: %d\n", g_pmu->type);
|
|
for (i = 0; i < g_pmu->events.nr; i++) {
|
|
PRINTK("events[%d]: %d, 0x%x\n", i, g_pmu->events.per[i].eventId, g_pmu->events.per[i].period);
|
|
}
|
|
PRINTK("predivided: %d\n", g_pmu->events.cntDivided);
|
|
} else {
|
|
PRINTK("pmu is NULL\n");
|
|
}
|
|
|
|
PRINTK("sampleType: 0x%x\n", g_perfCb.sampleType);
|
|
for (i = 0; i < g_perfCb.taskIdsNr; i++) {
|
|
PRINTK("filter taskIds[%d]: %d\n", i, g_perfCb.taskIds[i]);
|
|
}
|
|
for (i = 0; i < g_perfCb.processIdsNr; i++) {
|
|
PRINTK("filter processIds[%d]: %d\n", i, g_perfCb.processIds[i]);
|
|
}
|
|
PRINTK("needSample: %d\n", g_perfCb.needSample);
|
|
}
|
|
|
|
STATIC INLINE VOID OsPerfSetFilterIds(UINT32 *dstIds, UINT8 *dstIdsNr, UINT32 *ids, UINT32 idsNr)
|
|
{
|
|
errno_t ret;
|
|
if (idsNr) {
|
|
ret = memcpy_s(dstIds, PERF_MAX_FILTER_TSKS * sizeof(UINT32), ids, idsNr * sizeof(UINT32));
|
|
if (ret != EOK) {
|
|
PRINT_ERR("In %s At line:%d execute memcpy_s error\n", __FUNCTION__, __LINE__);
|
|
*dstIdsNr = 0;
|
|
return;
|
|
}
|
|
*dstIdsNr = MIN(idsNr, PERF_MAX_FILTER_TSKS);
|
|
} else {
|
|
*dstIdsNr = 0;
|
|
}
|
|
}
|
|
|
|
UINT32 LOS_PerfConfig(PerfConfigAttr *attr)
|
|
{
|
|
UINT32 ret;
|
|
UINT32 intSave;
|
|
|
|
if (attr == NULL) {
|
|
return LOS_ERRNO_PERF_CONFIG_NULL;
|
|
}
|
|
|
|
PERF_LOCK(intSave);
|
|
if (g_perfCb.status != PERF_STOPPED) {
|
|
ret = LOS_ERRNO_PERF_STATUS_INVALID;
|
|
PRINT_ERR("perf config status error : 0x%x\n", g_perfCb.status);
|
|
goto PERF_CONFIG_ERROR;
|
|
}
|
|
|
|
g_pmu = NULL;
|
|
|
|
g_perfCb.needSample = attr->needSample;
|
|
g_perfCb.sampleType = attr->sampleType;
|
|
|
|
OsPerfSetFilterIds(g_perfCb.taskIds, &g_perfCb.taskIdsNr, attr->taskIds, attr->taskIdsNr);
|
|
OsPerfSetFilterIds(g_perfCb.processIds, &g_perfCb.processIdsNr, attr->processIds, attr->processIdsNr);
|
|
|
|
ret = OsPerfConfig(&attr->eventsCfg);
|
|
PerfInfoDump();
|
|
PERF_CONFIG_ERROR:
|
|
PERF_UNLOCK(intSave);
|
|
return ret;
|
|
}
|
|
|
|
VOID LOS_PerfStart(UINT32 sectionId)
|
|
{
|
|
UINT32 intSave;
|
|
UINT32 ret;
|
|
|
|
PERF_LOCK(intSave);
|
|
if (g_perfCb.status != PERF_STOPPED) {
|
|
PRINT_ERR("perf start status error : 0x%x\n", g_perfCb.status);
|
|
goto PERF_START_ERROR;
|
|
}
|
|
|
|
if (!OsPerfParamValid()) {
|
|
PRINT_ERR("forgot call `LOS_PerfConfig(...)` before perf start?\n");
|
|
goto PERF_START_ERROR;
|
|
}
|
|
|
|
if (g_perfCb.needSample) {
|
|
ret = OsPerfHdrInit(sectionId); /* section header init */
|
|
if (ret != LOS_OK) {
|
|
PRINT_ERR("perf hdr init error 0x%x\n", ret);
|
|
goto PERF_START_ERROR;
|
|
}
|
|
}
|
|
|
|
SMP_CALL_PERF_FUNC(OsPerfStart); /* send to all cpu to start pmu */
|
|
g_perfCb.status = PERF_STARTED;
|
|
g_perfCb.startTime = OsPerfGetCurrTime();
|
|
PERF_START_ERROR:
|
|
PERF_UNLOCK(intSave);
|
|
return;
|
|
}
|
|
|
|
VOID LOS_PerfStop(VOID)
|
|
{
|
|
UINT32 intSave;
|
|
|
|
PERF_LOCK(intSave);
|
|
if (g_perfCb.status != PERF_STARTED) {
|
|
PRINT_ERR("perf stop status error : 0x%x\n", g_perfCb.status);
|
|
goto PERF_STOP_ERROR;
|
|
}
|
|
|
|
SMP_CALL_PERF_FUNC(OsPerfStop); /* send to all cpu to stop pmu */
|
|
|
|
OsPerfOutputFlush();
|
|
|
|
if (g_perfCb.needSample) {
|
|
OsPerfOutputInfo();
|
|
}
|
|
|
|
g_perfCb.status = PERF_STOPPED;
|
|
g_perfCb.endTime = OsPerfGetCurrTime();
|
|
|
|
OsPerfPrintTs();
|
|
PERF_STOP_ERROR:
|
|
PERF_UNLOCK(intSave);
|
|
return;
|
|
}
|
|
|
|
UINT32 LOS_PerfDataRead(CHAR *dest, UINT32 size)
|
|
{
|
|
return OsPerfOutputRead(dest, size);
|
|
}
|
|
|
|
VOID LOS_PerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func)
|
|
{
|
|
UINT32 intSave;
|
|
|
|
PERF_LOCK(intSave);
|
|
OsPerfNotifyHookReg(func);
|
|
PERF_UNLOCK(intSave);
|
|
}
|
|
|
|
VOID LOS_PerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func)
|
|
{
|
|
UINT32 intSave;
|
|
|
|
PERF_LOCK(intSave);
|
|
OsPerfFlushHookReg(func);
|
|
PERF_UNLOCK(intSave);
|
|
}
|
|
|
|
VOID OsPerfSetIrqRegs(UINTPTR pc, UINTPTR fp)
|
|
{
|
|
LosTaskCB *runTask = (LosTaskCB *)ArchCurrTaskGet();
|
|
runTask->pc = pc;
|
|
runTask->fp = fp;
|
|
}
|
|
|
|
LOS_MODULE_INIT(OsPerfInit, LOS_INIT_LEVEL_KMOD_EXTENDED);
|