diff --git a/apps/BUILD.gn b/apps/BUILD.gn index 6cc40b20e90145b7c78d939bd6ad6d40c0826103..eab6ff27ed654fa7953bf1a55303627a17af3743 100644 --- a/apps/BUILD.gn +++ b/apps/BUILD.gn @@ -34,8 +34,8 @@ group("apps") { if (defined(LOSCFG_SHELL)) { deps += [ - "shell", "mksh", + "shell", "toybox", ] } @@ -51,4 +51,8 @@ group("apps") { if (defined(LOSCFG_DRIVERS_TRACE)) { deps += [ "trace" ] } + + if (defined(LOSCFG_DRIVERS_PERF)) { + deps += [ "perf" ] + } } diff --git a/apps/config.mk b/apps/config.mk index 6e15ccab0099eb43a65ecf0941ebfa6055a7959f..3668875be96ab3da057f4230a8265450bc0a2626 100644 --- a/apps/config.mk +++ b/apps/config.mk @@ -69,3 +69,7 @@ endif ifeq ($(LOSCFG_DRIVERS_TRACE), y) APP_SUBDIRS += trace endif + +ifeq ($(LOSCFG_DRIVERS_PERF), y) +APP_SUBDIRS += perf +endif diff --git a/apps/perf/BUILD.gn b/apps/perf/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..c195375b4a43dc4545897624aad3c36c3606445b --- /dev/null +++ b/apps/perf/BUILD.gn @@ -0,0 +1,63 @@ +# 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. + +import("//kernel/liteos_a/liteos.gni") + +executable("perf") { + sources = [ + "src/main.c", + "src/option.c", + "src/perf.c", + "src/perf_list.c", + "src/perf_record.c", + "src/perf_stat.c", + ] + include_dirs = [ "include" ] + defines = [] + + if (defined(LOSCFG_PERF_HW_PMU)) { + defines += [ "LOSCFG_PERF_HW_PMU" ] + } + + if (defined(LOSCFG_PERF_TIMED_PMU)) { + defines += [ "LOSCFG_PERF_TIMED_PMU" ] + } + + if (defined(LOSCFG_PERF_SW_PMU)) { + defines += [ "LOSCFG_PERF_SW_PMU" ] + } + + if (defined(LOSCFG_FS_VFS)) { + defines += [ "LOSCFG_FS_VFS" ] + } + + defines += [ "LOSCFG_PERF_BUFFER_SIZE=$LOSCFG_PERF_BUFFER_SIZE" ] + + deps = [ "$LITEOSTHIRDPARTY/bounds_checking_function:libsec_static" ] +} diff --git a/apps/perf/Makefile b/apps/perf/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..8c920a2c7513c659620aa88b0fb18bd3c37df80b --- /dev/null +++ b/apps/perf/Makefile @@ -0,0 +1,62 @@ +# 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 $(APPSTOPDIR)/config.mk + +APP_NAME := $(notdir $(shell pwd)) + +SECUREC_DIR := $(LITEOSTHIRDPARTY)/bounds_checking_function + +LOCAL_SRCS = $(wildcard src/*.c) +LOCAL_SRCS += $(wildcard $(SECUREC_DIR)/src/*.c) + +LOCAL_INCLUDE := \ + -I include \ + -I $(SECUREC_DIR)/include + +LOCAL_FLAGS += $(LOCAL_INCLUDE) + +ifeq ($(LOSCFG_PERF_HW_PMU), y) +CFLAGS += -DLOSCFG_PERF_HW_PMU +endif + +ifeq ($(LOSCFG_PERF_TIMED_PMU), y) +CFLAGS += -DLOSCFG_PERF_TIMED_PMU +endif + +ifeq ($(LOSCFG_PERF_SW_PMU), y) +CFLAGS += -DLOSCFG_PERF_SW_PMU +endif + +ifeq ($(LOSCFG_FS_VFS), y) +CFLAGS += -DLOSCFG_FS_VFS +endif + +CFLAGS += -DLOSCFG_PERF_BUFFER_SIZE=$(LOSCFG_PERF_BUFFER_SIZE) +include $(APP) diff --git a/apps/perf/include/option.h b/apps/perf/include/option.h new file mode 100644 index 0000000000000000000000000000000000000000..e745bebf66edb9457b25a5d8aca430a0f529dccf --- /dev/null +++ b/apps/perf/include/option.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + + +#ifndef _OPTION_H +#define _OPTION_H + +#include "perf.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#define CMD_MAX_PARAMS 10 +typedef int (*CALL_BACK)(const char *argv); + +enum OptionType { + OPTION_TYPE_UINT, + OPTION_TYPE_STRING, + OPTION_TYPE_CALLBACK, +}; + +typedef struct { + int type; + const char *name; + const char **str; + unsigned int *value; + CALL_BACK cb; +} PerfOption; + +typedef struct { + const char *path; + char *params[CMD_MAX_PARAMS]; +} SubCmd; + +#define OPTION_END() {.name = ""} +#define OPTION_UINT(n, v) {.type = OPTION_TYPE_UINT, .name = (n), .value = (v)} +#define OPTION_STRING(n, s) {.type = OPTION_TYPE_STRING, .name = (n), .str = (s)} +#define OPTION_CALLBACK(n, c) {.type = OPTION_TYPE_CALLBACK, .name = (n), .cb = (c)} + +int ParseOptions(int argc, char **argv, PerfOption *opt, SubCmd *cmd); +int ParseEvents(const char *argv, PerfEventConfig *eventsCfg, unsigned int *len); +int ParseIds(const char *argv, int *arr, unsigned int *len); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif /* _OPTION_H */ diff --git a/apps/perf/include/perf.h b/apps/perf/include/perf.h new file mode 100644 index 0000000000000000000000000000000000000000..2eccb5e1c1bd9f3465dd41f563b2c752837ec34d --- /dev/null +++ b/apps/perf/include/perf.h @@ -0,0 +1,163 @@ +/* + * 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. + */ + + +#ifndef _PERF_H +#define _PERF_H + +#include + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#define PERF_MAX_EVENT 7 +#define PERF_MAX_FILTER_TSKS 32 + +#ifdef PERF_DEBUG +#define printf_debug(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define printf_debug(fmt, ...) +#endif + +/* + * Perf types + */ +enum PerfEventType { + PERF_EVENT_TYPE_HW, /* boards common hw events */ + PERF_EVENT_TYPE_TIMED, /* hrtimer timed events */ + PERF_EVENT_TYPE_SW, /* software trace events */ + PERF_EVENT_TYPE_RAW, /* boards special hw events, see enum PmuEventType in corresponding arch headfile */ + + PERF_EVENT_TYPE_MAX +}; + +/* + * Common hardware pmu events + */ +enum PmuHwId { + PERF_COUNT_HW_CPU_CYCLES = 0, /* cpu cycle event */ + PERF_COUNT_HW_INSTRUCTIONS, /* instruction event */ + PERF_COUNT_HW_DCACHE_REFERENCES, /* dcache access event */ + PERF_COUNT_HW_DCACHE_MISSES, /* dcache miss event */ + PERF_COUNT_HW_ICACHE_REFERENCES, /* icache access event */ + PERF_COUNT_HW_ICACHE_MISSES, /* icache miss event */ + PERF_COUNT_HW_BRANCH_INSTRUCTIONS, /* software change of pc event */ + PERF_COUNT_HW_BRANCH_MISSES, /* branch miss event */ + + PERF_COUNT_HW_MAX, +}; + +/* + * Common hrtimer timed events + */ +enum PmuTimedId { + PERF_COUNT_CPU_CLOCK = 0, /* hrtimer timed event */ +}; + +/* + * Common software pmu events + */ +enum PmuSwId { + PERF_COUNT_SW_TASK_SWITCH = 1, /* task switch event */ + PERF_COUNT_SW_IRQ_RESPONSE, /* irq response event */ + PERF_COUNT_SW_MEM_ALLOC, /* memory alloc event */ + PERF_COUNT_SW_MUX_PEND, /* mutex pend event */ + + PERF_COUNT_SW_MAX, +}; + +/* + * perf sample data types + * Config it through PerfConfigAttr->sampleType. + */ +enum PerfSampleType { + PERF_RECORD_CPU = 1U << 0, /* record current cpuid */ + PERF_RECORD_TID = 1U << 1, /* record current task id */ + PERF_RECORD_TYPE = 1U << 2, /* record event type */ + PERF_RECORD_PERIOD = 1U << 3, /* record event period */ + PERF_RECORD_TIMESTAMP = 1U << 4, /* record timestamp */ + PERF_RECORD_IP = 1U << 5, /* record instruction pointer */ + PERF_RECORD_CALLCHAIN = 1U << 6, /* record backtrace */ + PERF_RECORD_PID = 1U << 7, /* record current process id */ +}; + +/* + * perf configuration sub event information + * + * This structure is used to config specific events attributes. + */ +typedef struct { + unsigned int type; /* enum PerfEventType */ + struct { + unsigned int eventId; /* the specific event corresponds to the PerfEventType */ + unsigned int period; /* event period, for every "period"th occurrence of the event a + sample will be recorded */ + } events[PERF_MAX_EVENT]; /* perf event list */ + unsigned int eventsNr; /* total perf event number */ + size_t predivided; /* whether to prescaler (once every 64 counts), + which only take effect on cpu cycle hardware event */ +} PerfEventConfig; + +/* + * perf configuration main information + * + * This structure is used to set perf sampling attributes, including events, tasks and other information. + */ +typedef struct { + PerfEventConfig eventsCfg; /* perf event config */ + unsigned int taskIds[PERF_MAX_FILTER_TSKS]; /* perf task filter list (allowlist) */ + unsigned int taskIdsNr; /* task numbers of task filter allowlist, + if set 0 perf will sample all tasks */ + unsigned int processIds[PERF_MAX_FILTER_TSKS]; /* perf process filter list (allowlist) */ + unsigned int processIdsNr; /* process numbers of process filter allowlist, + if set 0 perf will sample all processes */ + unsigned int sampleType; /* type of data to sample defined in PerfSampleType */ + size_t needSample; /* whether to sample data */ +} PerfConfigAttr; + +void PerfUsage(void); +void PerfDumpAttr(PerfConfigAttr *attr); +int PerfConfig(int fd, PerfConfigAttr *attr); +void PerfStart(int fd, size_t sectionId); +void PerfStop(int fd); +ssize_t PerfRead(int fd, char *buf, size_t size); +void PerfPrintBuffer(const char *buf, ssize_t num); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif /* _PERF_H */ diff --git a/apps/perf/include/perf_list.h b/apps/perf/include/perf_list.h new file mode 100644 index 0000000000000000000000000000000000000000..a65e9d70be590054e9e714f9bbae842acc944d6c --- /dev/null +++ b/apps/perf/include/perf_list.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + + +#ifndef _PERF_LIST_H +#define _PERF_LIST_H + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +typedef struct { + const char *name; + int event; + int type; +} PerfEvent; + +extern const PerfEvent g_events[]; +void PerfList(void); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif /* _PERF_LIST_H */ diff --git a/apps/perf/include/perf_record.h b/apps/perf/include/perf_record.h new file mode 100644 index 0000000000000000000000000000000000000000..b218eb753b9fd043771c7b3f26468a047025fc5c --- /dev/null +++ b/apps/perf/include/perf_record.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef _PERF_RECORD_H +#define _PERF_RECORD_H + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +void PerfRecord(int fd, int argc, char **argv); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif /* _PERF_RECORD_H */ diff --git a/apps/perf/include/perf_stat.h b/apps/perf/include/perf_stat.h new file mode 100644 index 0000000000000000000000000000000000000000..ad2a456cde06ebdc29c0bca63a3255466a038e72 --- /dev/null +++ b/apps/perf/include/perf_stat.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef _PERF_STAT_H +#define _PERF_STAT_H + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +void PerfStat(int fd, int argc, char **argv); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif /* _PERF_STAT_H */ diff --git a/apps/perf/src/main.c b/apps/perf/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..1093a60d28b2b16744a72892de89a6163450ceb4 --- /dev/null +++ b/apps/perf/src/main.c @@ -0,0 +1,82 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include "perf.h" +#include "perf_list.h" +#include "perf_stat.h" +#include "perf_record.h" + +int main(int argc, char **argv) +{ +#define TWO_ARGS 2 +#define THREE_ARGS 3 + int fd = open("/dev/perf", O_RDWR); + if (fd == -1) { + printf("Perf open failed.\n"); + exit(EXIT_FAILURE); + } + + if (argc == 1) { + PerfUsage(); + } else if ((argc == TWO_ARGS) && strcmp(argv[1], "start") == 0) { + PerfStart(fd, 0); + } else if ((argc == THREE_ARGS) && strcmp(argv[1], "start") == 0) { + size_t id = strtoul(argv[THREE_ARGS - 1], NULL, 0); + PerfStart(fd, id); + } else if ((argc == TWO_ARGS) && strcmp(argv[1], "stop") == 0) { + PerfStop(fd); + } else if ((argc == THREE_ARGS) && strcmp(argv[1], "read") == 0) { + size_t size = strtoul(argv[THREE_ARGS - 1], NULL, 0); + char *buf = (char *)malloc(size); + int len = PerfRead(fd, buf, size); + PerfPrintBuffer(buf, len); + free(buf); + } else if ((argc == TWO_ARGS) && strcmp(argv[1], "list") == 0) { + PerfList(); + } else if ((argc >= THREE_ARGS) && strcmp(argv[1], "stat") == 0) { + PerfStat(fd, argc, argv); + } else if ((argc >= THREE_ARGS) && strcmp(argv[1], "record") == 0) { + PerfRecord(fd, argc, argv); + } else { + printf("Unsupported perf command.\n"); + PerfUsage(); + } + + close(fd); + return 0; +} diff --git a/apps/perf/src/option.c b/apps/perf/src/option.c new file mode 100644 index 0000000000000000000000000000000000000000..40ef343092c4b1c2bd241bf41de8862457c7b48d --- /dev/null +++ b/apps/perf/src/option.c @@ -0,0 +1,182 @@ +/* + * 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 +#include +#include "option.h" +#include "perf_list.h" + +static int ParseOption(char **argv, int *index, PerfOption *opts) +{ + int ret = 0; + const char *str = NULL; + + while ((opts->name != NULL) && (*opts->name != 0)) { + if (strcmp(argv[*index], opts->name) == 0) { + switch (opts->type) { + case OPTION_TYPE_UINT: + *opts->value = strtoul(argv[++(*index)], NULL, 0); + break; + case OPTION_TYPE_STRING: + *opts->str = argv[++(*index)]; + break; + case OPTION_TYPE_CALLBACK: + str = argv[++(*index)]; + if ((*opts->cb)(str) != 0) { + printf("parse error\n"); + ret = -1; + } + break; + default: + printf("invalid option\n"); + ret = -1; + break; + } + return ret; + } + opts++; + } + + return -1; +} + +int ParseOptions(int argc, char **argv, PerfOption *opts, SubCmd *cmd) +{ + int i; + int index = 0; + + while ((index < argc) && (argv[index] != NULL) && (*argv[index] == '-')) { + if (ParseOption(argv, &index, opts) != 0) { + return -1; + } + index++; + } + + if ((index < argc) && (argv[index] != NULL)) { + cmd->path = argv[index]; + cmd->params[0] = argv[index]; + index++; + } else { + printf("no subcmd to execute\n"); + return -1; + } + + for (i = 1; (index < argc) && (i < CMD_MAX_PARAMS); index++, i++) { + cmd->params[i] = argv[index]; + } + printf_debug("subcmd = %s\n", cmd->path); + for (int j = 0; j < i; j++) { + printf_debug("paras[%d]:%s\n", j, cmd->params[j]); + } + return 0; +} + +int ParseIds(const char *argv, int *arr, unsigned int *len) +{ + int res, ret; + unsigned int index = 0; + char *sp = NULL; + char *this = NULL; + char *list = strdup(argv); + + if (list == NULL) { + printf("no memory for ParseIds\n"); + return -1; + } + + sp = strtok_r(list, ",", &this); + while (sp) { + res = strtoul(sp, NULL, 0); + if (res < 0) { + ret = -1; + goto EXIT; + } + arr[index++] = res; + sp = strtok_r(NULL, ",", &this); + } + *len = index; + ret = 0; +EXIT: + free(list); + return ret; +} + +static inline const PerfEvent *StrToEvent(const char *str) +{ + const PerfEvent *evt = &g_events[0]; + + for (; evt->event != -1; evt++) { + if (strcmp(str, evt->name) == 0) { + return evt; + } + } + return NULL; +} + +int ParseEvents(const char *argv, PerfEventConfig *eventsCfg, unsigned int *len) +{ + int ret; + unsigned int index = 0; + const PerfEvent *event = NULL; + char *sp = NULL; + char *this = NULL; + char *list = strdup(argv); + + if (list == NULL) { + printf("no memory for ParseEvents\n"); + return -1; + } + + sp = strtok_r(list, ",", &this); + while (sp) { + event = StrToEvent(sp); + if (event == NULL) { + ret = -1; + goto EXIT; + } + + if (index == 0) { + eventsCfg->type = event->type; + } else if (eventsCfg->type != event->type) { + printf("events type must be same\n"); + ret = -1; + goto EXIT; + } + eventsCfg->events[index].eventId = event->event; + sp = strtok_r(NULL, ",", &this); + index++; + } + *len = index; + ret = 0; +EXIT: + free(list); + return ret; +} \ No newline at end of file diff --git a/apps/perf/src/perf.c b/apps/perf/src/perf.c new file mode 100644 index 0000000000000000000000000000000000000000..fdcf0b4ec583ddbe0dfc969a0f922341c9520e01 --- /dev/null +++ b/apps/perf/src/perf.c @@ -0,0 +1,136 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include "perf.h" + +#define PERF_IOC_MAGIC 'T' +#define PERF_START _IO(PERF_IOC_MAGIC, 1) +#define PERF_STOP _IO(PERF_IOC_MAGIC, 2) + +void PerfUsage(void) +{ + printf("\nUsage: ./perf start [id]. Start perf.\n"); + printf("\nUsage: ./perf stop. Stop perf.\n"); + printf("\nUsage: ./perf read . Read nBytes raw data from perf buffer and print out.\n"); + printf("\nUsage: ./perf list. List events to be used in -e.\n"); + printf("\nUsage: ./perf stat/record [option] . \n" + "-e, event selector. use './perf list' to list available events.\n" + "-p, event period.\n" + "-o, perf data output filename.\n" + "-t, taskId filter(allowlist), if not set perf will sample all tasks.\n" + "-s, type of data to sample defined in PerfSampleType los_perf.h.\n" + "-P, processId filter(allowlist), if not set perf will sample all processes.\n" + "-d, whether to prescaler (once every 64 counts)," + "which only take effect on cpu cycle hardware event.\n" + ); +} + +static void PerfSetPeriod(PerfConfigAttr *attr) +{ + int i; + for (i = 1; i < attr->eventsCfg.eventsNr; i++) { + attr->eventsCfg.events[i].period = attr->eventsCfg.events[0].period; + } +} + +void PerfPrintBuffer(const char *buf, ssize_t num) +{ +#define BYTES_PER_LINE 4 + ssize_t i = 0; + for (i = 0; i < num; i++) { + printf(" %02x", (unsigned char)buf[i]); + if (((i + 1) % BYTES_PER_LINE) == 0) { + printf("\n"); + } + } + printf("\n"); +} + +void PerfDumpAttr(PerfConfigAttr *attr) +{ + int i; + printf_debug("attr->type: %d\n", attr->eventsCfg.type); + for (i = 0; i < attr->eventsCfg.eventsNr; i++) { + printf_debug("attr->events[%d]: %d, 0x%x\n", i, attr->eventsCfg.events[i].eventId, + attr->eventsCfg.events[i].period); + } + printf_debug("attr->predivided: %d\n", attr->eventsCfg.predivided); + printf_debug("attr->sampleType: 0x%x\n", attr->sampleType); + + for (i = 0; i < attr->taskIdsNr; i++) { + printf_debug("attr->taskIds[%d]: %d\n", i, attr->taskIds[i]); + } + + for (i = 0; i < attr->processIdsNr; i++) { + printf_debug("attr->processIds[%d]: %d\n", i, attr->processIds[i]); + } + + printf_debug("attr->needSample: %d\n", attr->needSample); +} + + +void PerfStart(int fd, size_t sectionId) +{ + (void)ioctl(fd, PERF_START, sectionId); +} + +void PerfStop(int fd) +{ + (void)ioctl(fd, PERF_STOP, NULL); +} + +int PerfConfig(int fd, PerfConfigAttr *attr) +{ + if (attr == NULL) { + return -1; + } + PerfSetPeriod(attr); + PerfDumpAttr(attr); + return write(fd, attr, sizeof(PerfConfigAttr)); +} + +ssize_t PerfRead(int fd, char *buf, size_t size) +{ + ssize_t len; + if (buf == NULL) { + printf("Read buffer is null.\n"); + return 0; + } + + len = read(fd, buf, size); + return len; +} \ No newline at end of file diff --git a/apps/perf/src/perf_list.c b/apps/perf/src/perf_list.c new file mode 100644 index 0000000000000000000000000000000000000000..73e6a6e8722c2f32a8997047e622bf71854404a8 --- /dev/null +++ b/apps/perf/src/perf_list.c @@ -0,0 +1,129 @@ +/* + * 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 +#include "perf.h" +#include "perf_list.h" + +static const char *g_eventTypeStr[] = { + "[Hardware event]", + "[Timed event]", + "[Software event]", +}; + +const PerfEvent g_events[] = { +#ifdef LOSCFG_PERF_HW_PMU + { + .name = "cycles", + .event = PERF_COUNT_HW_CPU_CYCLES, + .type = PERF_EVENT_TYPE_HW, + }, + { + .name = "instruction", + .event = PERF_COUNT_HW_INSTRUCTIONS, + .type = PERF_EVENT_TYPE_HW, + }, + { + .name = "dcache", + .event = PERF_COUNT_HW_DCACHE_REFERENCES, + .type = PERF_EVENT_TYPE_HW, + }, + { + .name = "dcache-miss", + .event = PERF_COUNT_HW_DCACHE_MISSES, + .type = PERF_EVENT_TYPE_HW, + }, + { + .name = "icache", + .event = PERF_COUNT_HW_ICACHE_REFERENCES, + .type = PERF_EVENT_TYPE_HW, + }, + { + .name = "icache-miss", + .event = PERF_COUNT_HW_ICACHE_MISSES, + .type = PERF_EVENT_TYPE_HW, + }, + { + .name = "branch", + .event = PERF_COUNT_HW_BRANCH_INSTRUCTIONS, + .type = PERF_EVENT_TYPE_HW, + }, + { + .name = "branch-miss", + .event = PERF_COUNT_HW_BRANCH_MISSES, + .type = PERF_EVENT_TYPE_HW, + }, +#endif +#ifdef LOSCFG_PERF_TIMED_PMU + { + .name = "clock", + .event = PERF_COUNT_CPU_CLOCK, + .type = PERF_EVENT_TYPE_TIMED, + }, +#endif +#ifdef LOSCFG_PERF_SW_PMU + { + .name = "task-switch", + .event = PERF_COUNT_SW_TASK_SWITCH, + .type = PERF_EVENT_TYPE_SW, + }, + { + .name = "irq-in", + .event = PERF_COUNT_SW_IRQ_RESPONSE, + .type = PERF_EVENT_TYPE_SW, + }, + { + .name = "mem-alloc", + .event = PERF_COUNT_SW_MEM_ALLOC, + .type = PERF_EVENT_TYPE_SW, + }, + { + .name = "mux-pend", + .event = PERF_COUNT_SW_MUX_PEND, + .type = PERF_EVENT_TYPE_SW, + }, +#endif + { + .name = "", + .event = -1, + .type = PERF_EVENT_TYPE_MAX, + } +}; + +void PerfList(void) +{ + const PerfEvent *evt = &g_events[0]; + printf("\n"); + for (; evt->event != -1; evt++) { + printf("\t %-25s%30s\n", evt->name, g_eventTypeStr[evt->type]); + } + printf("\n"); +} diff --git a/apps/perf/src/perf_record.c b/apps/perf/src/perf_record.c new file mode 100644 index 0000000000000000000000000000000000000000..28421001213effe7ba856a1fedbdb45373acb169 --- /dev/null +++ b/apps/perf/src/perf_record.c @@ -0,0 +1,204 @@ +/* + * 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 +#include +#include + +#ifdef LOSCFG_FS_VFS +#include +#include +#endif + +#include "perf.h" +#include "option.h" +#include "perf_record.h" + +#define PERF_FILE_MODE 0644 +static PerfConfigAttr g_recordAttr; +static const char *g_savePath = "/storage/data/perf.data"; + +static inline int GetEvents(const char *argv) +{ + return ParseEvents(argv, &g_recordAttr.eventsCfg, &g_recordAttr.eventsCfg.eventsNr); +} + +static inline int GetTids(const char *argv) +{ + return ParseIds(argv, (int *)g_recordAttr.taskIds, &g_recordAttr.taskIdsNr); +} + +static inline int GetPids(const char *argv) +{ + return ParseIds(argv, (int *)g_recordAttr.processIds, &g_recordAttr.processIdsNr); +} + +static PerfOption g_recordOpts[] = { + OPTION_CALLBACK("-e", GetEvents), + OPTION_CALLBACK("-t", GetTids), + OPTION_CALLBACK("-P", GetPids), + OPTION_STRING("-o", &g_savePath), + OPTION_UINT("-p", &g_recordAttr.eventsCfg.events[0].period), + OPTION_UINT("-s", &g_recordAttr.sampleType), + OPTION_UINT("-d", &g_recordAttr.eventsCfg.predivided), +}; + +static int PerfRecordAttrInit(void) +{ + PerfConfigAttr attr = { + .eventsCfg = { +#ifdef LOSCFG_PERF_HW_PMU + .type = PERF_EVENT_TYPE_HW, + .events = { + [0] = {PERF_COUNT_HW_CPU_CYCLES, 0xFFFF}, + }, +#elif defined LOSCFG_PERF_TIMED_PMU + .type = PERF_EVENT_TYPE_TIMED, + .events = { + [0] = {PERF_COUNT_CPU_CLOCK, 100}, + }, +#elif defined LOSCFG_PERF_SW_PMU + .type = PERF_EVENT_TYPE_SW, + .events = { + [0] = {PERF_COUNT_SW_TASK_SWITCH, 1}, + }, +#endif + .eventsNr = 1, /* 1 event */ + .predivided = 0, + }, + .taskIds = {0}, + .taskIdsNr = 0, + .processIds = {0}, + .processIdsNr = 0, + .needSample = 1, + .sampleType = PERF_RECORD_IP | PERF_RECORD_CALLCHAIN, + }; + + return memcpy_s(&g_recordAttr, sizeof(PerfConfigAttr), &attr, sizeof(PerfConfigAttr)) != EOK ? -1 : 0; +} + +ssize_t PerfWriteFile(const char *filePath, const char *buf, ssize_t bufSize) +{ +#ifdef LOSCFG_FS_VFS + int fd = -1; + ssize_t totalToWrite = bufSize; + ssize_t totalWrite = 0; + + if (filePath == NULL || buf == NULL || bufSize == 0) { + printf("filePath: %p, buf: %p, bufSize: %u!\n", filePath, buf, bufSize); + return -1; + } + + fd = open(filePath, O_CREAT | O_RDWR | O_TRUNC, PERF_FILE_MODE); + if (fd < 0) { + printf("create file [%s] failed, fd: %d, %s!\n", filePath, fd, strerror(errno)); + return -1; + } + while (totalToWrite > 0) { + ssize_t writeThisTime = write(fd, buf, totalToWrite); + if (writeThisTime < 0) { + printf("failed to write file [%s], %s!\n", filePath, strerror(errno)); + (void)close(fd); + return -1; + } + buf += writeThisTime; + totalToWrite -= writeThisTime; + totalWrite += writeThisTime; + } + (void)fsync(fd); + (void)close(fd); + + return (totalWrite == bufSize) ? 0 : -1; +#else + (void)filePath; + PerfPrintBuffer(buf, bufSize); + return 0; +#endif +} + +void PerfRecord(int fd, int argc, char **argv) +{ + int ret; + int child; + char *buf; + ssize_t len; + SubCmd cmd = {0}; + + if (argc < 3) { /* perf record argc is at least 3 */ + return; + } + + ret = PerfRecordAttrInit(); + if (ret != 0) { + printf("perf record attr init failed\n"); + return; + } + + ret = ParseOptions(argc - 2, &argv[2], g_recordOpts, &cmd); /* parse option and cmd begin at index 2 */ + if (ret != 0) { + printf("parse error\n"); + return; + } + + ret = PerfConfig(fd, &g_recordAttr); + if (ret != 0) { + printf("perf config failed\n"); + return; + } + + PerfStart(fd, 0); + child = fork(); + if (child < 0) { + printf("fork error\n"); + PerfStop(fd); + return; + } else if (child == 0) { + (void)execve(cmd.path, cmd.params, NULL); + exit(0); + } + + waitpid(child, 0, 0); + PerfStop(fd); + + buf = (char *)malloc(LOSCFG_PERF_BUFFER_SIZE); + if (buf == NULL) { + printf("no memory for read perf 0x%x\n", LOSCFG_PERF_BUFFER_SIZE); + return; + } + len = PerfRead(fd, buf, LOSCFG_PERF_BUFFER_SIZE); + ret = PerfWriteFile(g_savePath, buf, len); + if (ret == 0) { + printf("save perf data success at %s\n", g_savePath); + } else { + printf("save perf data failed at %s\n", g_savePath); + } + free(buf); +} diff --git a/apps/perf/src/perf_stat.c b/apps/perf/src/perf_stat.c new file mode 100644 index 0000000000000000000000000000000000000000..a1155048572355abe6aeb4365d5269b8b1606034 --- /dev/null +++ b/apps/perf/src/perf_stat.c @@ -0,0 +1,149 @@ +/* + * 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 +#include +#include +#include "perf.h" +#include "option.h" +#include "perf_stat.h" + +static PerfConfigAttr g_statAttr; + +static inline int GetEvents(const char *argv) +{ + return ParseEvents(argv, &g_statAttr.eventsCfg, &g_statAttr.eventsCfg.eventsNr); +} + +static inline int GetTids(const char *argv) +{ + return ParseIds(argv, (int *)g_statAttr.taskIds, &g_statAttr.taskIdsNr); +} + +static inline int GetPids(const char *argv) +{ + return ParseIds(argv, (int *)g_statAttr.processIds, &g_statAttr.processIdsNr); +} + +static PerfOption g_statOpts[] = { + OPTION_CALLBACK("-e", GetEvents), + OPTION_CALLBACK("-t", GetTids), + OPTION_CALLBACK("-P", GetPids), + OPTION_UINT("-p", &g_statAttr.eventsCfg.events[0].period), + OPTION_UINT("-s", &g_statAttr.sampleType), + OPTION_UINT("-d", &g_statAttr.eventsCfg.predivided), +}; + +static int PerfStatAttrInit(void) +{ + PerfConfigAttr attr = { + .eventsCfg = { +#ifdef LOSCFG_PERF_HW_PMU + .type = PERF_EVENT_TYPE_HW, + .events = { + [0] = {PERF_COUNT_HW_CPU_CYCLES, 0xFFFF}, + [1] = {PERF_COUNT_HW_INSTRUCTIONS, 0xFFFFFF00}, + [2] = {PERF_COUNT_HW_ICACHE_REFERENCES, 0xFFFF}, + [3] = {PERF_COUNT_HW_DCACHE_REFERENCES, 0xFFFF}, + }, + .eventsNr = 4, /* 4 events */ +#elif defined LOSCFG_PERF_TIMED_PMU + .type = PERF_EVENT_TYPE_TIMED, + .events = { + [0] = {PERF_COUNT_CPU_CLOCK, 100}, + }, + .eventsNr = 1, /* 1 event */ +#elif defined LOSCFG_PERF_SW_PMU + .type = PERF_EVENT_TYPE_SW, + .events = { + [0] = {PERF_COUNT_SW_TASK_SWITCH, 1}, + [1] = {PERF_COUNT_SW_IRQ_RESPONSE, 1}, + [2] = {PERF_COUNT_SW_MEM_ALLOC, 1}, + [3] = {PERF_COUNT_SW_MUX_PEND, 1}, + }, + .eventsNr = 4, /* 4 events */ +#endif + .predivided = 0, + }, + .taskIds = {0}, + .taskIdsNr = 0, + .processIds = {0}, + .processIdsNr = 0, + .needSample = 0, + .sampleType = 0, + }; + + return memcpy_s(&g_statAttr, sizeof(PerfConfigAttr), &attr, sizeof(PerfConfigAttr)) != EOK ? -1 : 0; +} + +void PerfStat(int fd, int argc, char **argv) +{ + int ret; + int child; + SubCmd cmd = {0}; + + if (argc < 3) { /* perf stat argc is at least 3 */ + return; + } + + ret = PerfStatAttrInit(); + if (ret != 0) { + printf("perf stat attr init failed\n"); + return; + } + + ret = ParseOptions(argc - 2, &argv[2], g_statOpts, &cmd); /* parse option and cmd begin at index 2 */ + if (ret != 0) { + printf("parse error\n"); + return; + } + + ret = PerfConfig(fd, &g_statAttr); + if (ret != 0) { + printf("perf config failed\n"); + return; + } + + PerfStart(fd, 0); + child = fork(); + if (child < 0) { + printf("fork error\n"); + goto EXIT; + } else if (child == 0) { + (void)execve(cmd.path, cmd.params, NULL); + exit(0); + } + + (void)waitpid(child, 0, 0); +EXIT: + PerfStop(fd); +} + diff --git a/arch/arm/arm/BUILD.gn b/arch/arm/arm/BUILD.gn index 737f53a2f073eafcb37c9afdb4dcf2357c0130e5..260544f6d2d73dd92484cd8a9960b3b99e49cd65 100644 --- a/arch/arm/arm/BUILD.gn +++ b/arch/arm/arm/BUILD.gn @@ -46,10 +46,10 @@ kernel_module(module_name) { "src/los_hw_runstop.S", "src/los_hw_tick.c", "src/los_hwi.c", + "src/smp.c", "src/strncpy_from_user.c", "src/strnlen_user.c", "src/user_copy.c", - "src/smp.c", ] if (LOSCFG_ARCH_ARM_VER == "armv7-a") { @@ -64,6 +64,10 @@ kernel_module(module_name) { include_dirs = [ "src/include" ] + if (defined(LOSCFG_PERF_HW_PMU)) { + sources += [ "src/pmu/armv7_pmu.c" ] + } + if (defined(LOSCFG_GDB)) { configs += [ ":as_objs_libc_flags" ] } @@ -82,9 +86,11 @@ config("as_objs_libc_flags") { defines = [ "__ASSEMBLY__" ] # linux style macros - if (defined(LOSCFG_ARCH_ARM_V7A) || defined(LOSCFG_ARCH_ARM_V7R) || defined(LOSCFG_ARCH_ARM_V7M)) { + if (defined(LOSCFG_ARCH_ARM_V7A) || defined(LOSCFG_ARCH_ARM_V7R) || + defined(LOSCFG_ARCH_ARM_V7M)) { defines += [ "__LINUX_ARM_ARCH__=7" ] - } else if (defined(LOSCFG_ARCH_ARM_V8A) || defined(LOSCFG_ARCH_ARM_V8R) || defined(LOSCFG_ARCH_ARM_V8M)) { + } else if (defined(LOSCFG_ARCH_ARM_V8A) || defined(LOSCFG_ARCH_ARM_V8R) || + defined(LOSCFG_ARCH_ARM_V8M)) { defines += [ "__LINUX_ARM_ARCH__=8" ] } } diff --git a/arch/arm/arm/Makefile b/arch/arm/arm/Makefile index f6cb6a1ec3f722218045833fb91e0e96854602af..52f8b55bf494464c68c5ca933132db5cf11dc217 100644 --- a/arch/arm/arm/Makefile +++ b/arch/arm/arm/Makefile @@ -43,6 +43,12 @@ else LOCAL_SRCS += src/startup/reset_vector_up.S endif +ifeq ($(LOSCFG_PERF_HW_PMU), y) +LOCAL_SRCS += src/pmu/armv7_pmu.c +endif + +LOCAL_FLAGS := $(LOCAL_INCLUDE) + AS_OBJS_LIBC_FLAGS = -D__ASSEMBLY__ # linux style macros LINUX_ARCH_$(LOSCFG_ARCH_ARM_V7A) = -D__LINUX_ARM_ARCH__=7 @@ -54,6 +60,6 @@ LINUX_ARCH_$(LOSCFG_ARCH_ARM_V8M) = -D__LINUX_ARM_ARCH__=8 AS_OBJS_LIBC_FLAGS += $(LINUX_ARCH_y) ifeq ($(LOSCFG_GDB), y) -LOCAL_FLAGS := $(AS_OBJS_LIBC_FLAGS) +LOCAL_FLAGS += $(AS_OBJS_LIBC_FLAGS) endif include $(MODULE) diff --git a/arch/arm/arm/src/include/armv7_pmu_pri.h b/arch/arm/arm/src/include/armv7_pmu_pri.h new file mode 100644 index 0000000000000000000000000000000000000000..cb712f455ec448c6d144318e9c971f70b0b0ee3a --- /dev/null +++ b/arch/arm/arm/src/include/armv7_pmu_pri.h @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#ifndef _ARMV7_PMU_PRI_H +#define _ARMV7_PMU_PRI_H + +#include "los_typedef.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +/* counters overflow flag status reg */ +#define ARMV7_FLAG_MASK 0xffffffff /* Mask for writable bits */ +#define ARMV7_OVERFLOWED_MASK ARMV7_FLAG_MASK /* Mask for pmu overflowed */ + +/* pmnc config reg */ +#define ARMV7_PMNC_E (1U << 0) /* Enable all counters */ +#define ARMV7_PMNC_P (1U << 1) /* Reset all counters */ +#define ARMV7_PMNC_C (1U << 2) /* Cycle counter reset */ +#define ARMV7_PMNC_D (1U << 3) /* CCNT counts every 64th cpu cycle */ +#define ARMV7_PMNC_X (1U << 4) /* Export to ETM */ +#define ARMV7_PMNC_DP (1U << 5) /* Disable CCNT if non-invasive debug */ +#define ARMV7_PMNC_MASK 0x3f /* Mask for writable bits */ + +/* pmxevtyper event selection reg */ +#define ARMV7_EVTYPE_MASK 0xc80000ff /* Mask for writable bits */ + +/* armv7 counters index */ +#define ARMV7_IDX_COUNTER0 1 +#define ARMV7_IDX_CYCLE_COUNTER 0 +#define ARMV7_IDX_MAX_COUNTER 9 + +#define ARMV7_MAX_COUNTERS 32 +#define ARMV7_IDX_COUNTER_LAST (ARMV7_IDX_CYCLE_COUNTER + ARMV7_MAX_COUNTERS - 1) +#define ARMV7_COUNTER_MASK (ARMV7_MAX_COUNTERS - 1) + +/* armv7 event counter index mapping */ +#define ARMV7_CNT2BIT(x) (1UL << (x)) +#define ARMV7_IDX2CNT(x) (((x) - ARMV7_IDX_COUNTER0) & ARMV7_COUNTER_MASK) + +enum PmuEventType { + ARMV7_PERF_HW_CYCLES = 0xFF, /* cycles */ + ARMV7_PERF_HW_INSTRUCTIONS = 0x08, /* instructions */ + ARMV7_PERF_HW_DCACHES = 0x04, /* dcache */ + ARMV7_PERF_HW_DCACHE_MISSES = 0x03, /* dcache-misses */ + ARMV7_PERF_HW_ICACHES = 0x14, /* icache */ + ARMV7_PERF_HW_ICACHE_MISSES = 0x01, /* icache-misses */ + ARMV7_PERF_HW_BRANCHES = 0x0C, /* software change of pc */ + ARMV7_PERF_HW_BRANCE_MISSES = 0x10, /* branch-misses */ + ARMV7_PERF_HW_PRED_BRANCH = 0x12, /* predictable branches */ + ARMV7_PERF_HW_NUM_CYC_IRQ = 0x50, /* number of cycles Irqs are interrupted */ + ARMV7_PERF_HW_EXC_TAKEN = 0x09, /* exception_taken */ + ARMV7_PERF_HW_DATA_READ = 0x06, /* data read */ + ARMV7_PERF_HW_DATA_WRITE = 0x07, /* data write */ + ARMV7_PERF_HW_STREX_PASSED = 0x80, /* strex passed */ + ARMV7_PERF_HW_STREX_FAILED = 0x81, /* strex failed */ + ARMV7_PERF_HW_LP_IN_TCM = 0x82, /* literal pool in TCM region */ + ARMV7_PERF_HW_DMB_STALL = 0x90, /* DMB stall */ + ARMV7_PERF_HW_ITCM_ACCESS = 0x91, /* ITCM access */ + ARMV7_PERF_HW_DTCM_ACCESS = 0x92, /* DTCM access */ + ARMV7_PERF_HW_DATA_EVICTION = 0x93, /* data eviction */ + ARMV7_PERF_HW_SCU = 0x94, /* SCU coherency operation */ + ARMV7_PERF_HW_INSCACHE_DEP_DW = 0x95, /* instruction cache dependent stall */ + ARMV7_PERF_HW_DATA_CACHE_DEP_STALL = 0x96, /* data cache dependent stall */ + ARMV7_PERF_HW_NOCACHE_NO_PER_DEP_STALL = 0x97, /* non-cacheable no peripheral dependent stall */ + ARMV7_PERF_HW_NOCACHE_PER_DEP_STALL = 0x98, /* non-Cacheable peripheral dependent stall */ + ARMV7_PERF_HW_DATA_CACHE_HP_DEP_STALL = 0x99, /* data cache high priority dependent stall */ + ARMV7_PERF_HW_AXI_FAST_PERIPHERAL = 0x9A, /* Accesses_to_AXI_fast_peripheral_port(reads_and_writes) */ +}; + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif /* _ARMV7_PMU_PRI_H */ diff --git a/arch/arm/arm/src/include/los_exc_pri.h b/arch/arm/arm/src/include/los_exc_pri.h index 84e37d0ac5a2b7ba6b533ee00643b18b6645a41f..c80ebfc747cef38ee15f977d1c60bca12987b595 100644 --- a/arch/arm/arm/src/include/los_exc_pri.h +++ b/arch/arm/arm/src/include/los_exc_pri.h @@ -44,10 +44,24 @@ extern "C" { #define OS_SYSTEM_EXC_CURR_CPU 1 #define OS_SYSTEM_EXC_OTHER_CPU 2 +#define REGION_PATH_MAX 32 + +typedef struct { +#ifdef LOSCFG_KERNEL_VM + UINTPTR ip; + UINT32 len; /* f_path length */ + CHAR f_path[REGION_PATH_MAX]; +#else + UINTPTR ip; +#endif +} IpInfo; + extern UINT32 OsGetSystemStatus(VOID); extern VOID BackTraceSub(UINTPTR regFP); extern VOID OsExcInit(VOID); extern BOOL OsSystemExcIsReset(VOID); +extern UINT32 BackTraceGet(UINTPTR regFP, IpInfo *callChain, UINT32 maxDepth); +extern BOOL OsGetUsrIpInfo(UINTPTR ip, IpInfo *info); #ifdef __cplusplus #if __cplusplus diff --git a/arch/arm/arm/src/los_dispatch.S b/arch/arm/arm/src/los_dispatch.S index 3b7df79cd55dbcfb193e3f5c980371e703a455aa..797d167757e90f1aba21838d24d81e8e4a80517f 100644 --- a/arch/arm/arm/src/los_dispatch.S +++ b/arch/arm/arm/src/los_dispatch.S @@ -147,6 +147,14 @@ OsIrqHandler: /* disable irq, switch to svc mode */ CPSID i, #0x13 +#ifdef LOSCFG_KERNEL_PERF + PUSH {R0-R3, R12, LR} + MOV R0, LR + MOV R1, FP + BL OsPerfSetIrqRegs + POP {R0-R3, R12, LR} +#endif + STMFD SP!, {R0-R3, R12, LR} STMFD SP, {R13, R14}^ SUB SP, SP, #(4 * 4) diff --git a/arch/arm/arm/src/los_exc.c b/arch/arm/arm/src/los_exc.c index 297a061bd1713b70bc4d26347de4004f0701c621..a52d707bf995b990418a38caec0d8f22dc85d379 100644 --- a/arch/arm/arm/src/los_exc.c +++ b/arch/arm/arm/src/los_exc.c @@ -690,20 +690,66 @@ FOUND: return found; } -VOID BackTraceSub(UINTPTR regFP) +BOOL OsGetUsrIpInfo(UINTPTR ip, IpInfo *info) +{ + if (info == NULL) { + return FALSE; + } +#ifdef LOSCFG_KERNEL_VM + BOOL ret = FALSE; + const CHAR *name = NULL; + LosVmMapRegion *region = NULL; + LosProcessCB *runProcess = OsCurrProcessGet(); + + if (LOS_IsUserAddress((VADDR_T)ip) == FALSE) { + info->ip = ip; + name = "kernel"; + ret = FALSE; + goto END; + } + + region = LOS_RegionFind(runProcess->vmSpace, (VADDR_T)ip); + if (region == NULL) { + info->ip = ip; + name = "invalid"; + ret = FALSE; + goto END; + } + + info->ip = ip - OsGetTextRegionBase(region, runProcess); + name = OsGetRegionNameOrFilePath(region); + ret = TRUE; + if (strcmp(name, "/lib/libc.so") != 0) { + PRINT_ERR("ip = 0x%x, %s\n", info->ip, name); + } +END: + info->len = strlen(name); + if (strncpy_s(info->f_path, REGION_PATH_MAX, name, REGION_PATH_MAX - 1) != EOK) { + info->f_path[0] = '\0'; + info->len = 0; + PRINT_ERR("copy f_path failed, %s\n", name); + } + return ret; +#else + info->ip = ip; + return FALSE; +#endif +} + +UINT32 BackTraceGet(UINTPTR regFP, IpInfo *callChain, UINT32 maxDepth) { UINTPTR tmpFP, backLR; UINTPTR stackStart, stackEnd; UINTPTR backFP = regFP; UINT32 count = 0; + BOOL ret; VADDR_T kvaddr; -#ifdef LOSCFG_KERNEL_VM - LosProcessCB *runProcess = OsCurrProcessGet(); -#endif if (FindSuitableStack(regFP, &stackStart, &stackEnd, &kvaddr) == FALSE) { - PrintExcInfo("traceback error fp = 0x%x\n", regFP); - return; + if (callChain == NULL) { + PrintExcInfo("traceback error fp = 0x%x\n", regFP); + } + return 0; } /* @@ -715,7 +761,9 @@ VOID BackTraceSub(UINTPTR regFP) tmpFP = *(UINTPTR *)(UINTPTR)kvaddr; if (IsValidFP(tmpFP, stackStart, stackEnd, NULL) == TRUE) { backFP = tmpFP; - PrintExcInfo("traceback fp fixed, trace using fp = 0x%x\n", backFP); + if (callChain == NULL) { + PrintExcInfo("traceback fp fixed, trace using fp = 0x%x\n", backFP); + } } while (IsValidFP(backFP, stackStart, stackEnd, &kvaddr) == TRUE) { @@ -723,38 +771,49 @@ VOID BackTraceSub(UINTPTR regFP) #ifdef LOSCFG_COMPILER_CLANG_LLVM backFP = *(UINTPTR *)(UINTPTR)kvaddr; if (IsValidFP(tmpFP + POINTER_SIZE, stackStart, stackEnd, &kvaddr) == FALSE) { - PrintExcInfo("traceback backLR check failed, backLP: 0x%x\n", tmpFP + POINTER_SIZE); - return; + if (callChain == NULL) { + PrintExcInfo("traceback backLR check failed, backLP: 0x%x\n", tmpFP + POINTER_SIZE); + } + return 0; } backLR = *(UINTPTR *)(UINTPTR)kvaddr; #else backLR = *(UINTPTR *)(UINTPTR)kvaddr; if (IsValidFP(tmpFP - POINTER_SIZE, stackStart, stackEnd, &kvaddr) == FALSE) { - PrintExcInfo("traceback backFP check failed, backFP: 0x%x\n", tmpFP - POINTER_SIZE); - return; + if (callChain == NULL) { + PrintExcInfo("traceback backFP check failed, backFP: 0x%x\n", tmpFP - POINTER_SIZE); + } + return 0; } backFP = *(UINTPTR *)(UINTPTR)kvaddr; #endif + IpInfo info = {0}; + ret = OsGetUsrIpInfo((VADDR_T)backLR, &info); + if (callChain == NULL) { + PrintExcInfo("traceback %u -- lr = 0x%x fp = 0x%x ", count, backLR, backFP); + if (ret) { #ifdef LOSCFG_KERNEL_VM - LosVmMapRegion *region = NULL; - if (LOS_IsUserAddress((VADDR_T)backLR) == TRUE) { - region = LOS_RegionFind(runProcess->vmSpace, (VADDR_T)backLR); - } - if (region != NULL) { - PrintExcInfo("traceback %u -- lr = 0x%x fp = 0x%x lr in %s --> 0x%x\n", count, backLR, backFP, - OsGetRegionNameOrFilePath(region), - backLR - OsGetTextRegionBase(region, runProcess)); - region = NULL; - } else + PrintExcInfo("lr in %s --> 0x%x\n", info.f_path, info.ip); +#else + PrintExcInfo("\n"); #endif - { - PrintExcInfo("traceback %u -- lr = 0x%x fp = 0x%x\n", count, backLR, backFP); + } else { + PrintExcInfo("\n"); + } + } else { + (VOID)memcpy_s(&callChain[count], sizeof(IpInfo), &info, sizeof(IpInfo)); } count++; - if ((count == OS_MAX_BACKTRACE) || (backFP == tmpFP)) { + if ((count == maxDepth) || (backFP == tmpFP)) { break; } } + return count; +} + +VOID BackTraceSub(UINTPTR regFP) +{ + (VOID)BackTraceGet(regFP, NULL, OS_MAX_BACKTRACE); } VOID BackTrace(UINT32 regFP) diff --git a/arch/arm/arm/src/pmu/armv7_pmu.c b/arch/arm/arm/src/pmu/armv7_pmu.c new file mode 100644 index 0000000000000000000000000000000000000000..df17555bca8289ea7a264299ec699a3d29e7c038 --- /dev/null +++ b/arch/arm/arm/src/pmu/armv7_pmu.c @@ -0,0 +1,373 @@ +/* + * 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 "armv7_pmu_pri.h" +#include "perf_pmu_pri.h" +#include "los_hw_cpu.h" +#include "asm/platform.h" + +OS_PMU_INTS(LOSCFG_KERNEL_CORE_NUM, g_pmuIrqNr); +STATIC HwPmu g_armv7Pmu; + +STATIC INLINE UINT32 Armv7PmncRead(VOID) +{ + UINT32 value = 0; + __asm__ volatile("mrc p15, 0, %0, c9, c12, 0" : "=r"(value)); + return value; +} + +STATIC INLINE VOID Armv7PmncWrite(UINT32 value) +{ + value &= ARMV7_PMNC_MASK; + __asm__ volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(value)); + ISB; +} + +STATIC INLINE UINT32 Armv7PmuOverflowed(UINT32 pmnc) +{ + return pmnc & ARMV7_OVERFLOWED_MASK; +} + +STATIC INLINE UINT32 Armv7PmuCntOverflowed(UINT32 pmnc, UINT32 index) +{ + return pmnc & ARMV7_CNT2BIT(ARMV7_IDX2CNT(index)); +} + +STATIC INLINE UINT32 Armv7CntValid(UINT32 index) +{ + return index <= ARMV7_IDX_COUNTER_LAST; +} + +STATIC INLINE VOID Armv7PmuSelCnt(UINT32 index) +{ + UINT32 counter = ARMV7_IDX2CNT(index); + __asm__ volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter)); + ISB; +} + +STATIC INLINE VOID Armv7PmuSetCntPeriod(UINT32 index, UINT32 period) +{ + if (!Armv7CntValid(index)) { + PRINT_ERR("CPU writing wrong counter %u\n", index); + } else if (index == ARMV7_IDX_CYCLE_COUNTER) { + __asm__ volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (period)); + } else { + Armv7PmuSelCnt(index); + __asm__ volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (period)); + } +} + +STATIC INLINE VOID Armv7BindEvt2Cnt(UINT32 index, UINT32 value) +{ + PRINT_DEBUG("bind event: %u to counter: %u\n", value, index); + Armv7PmuSelCnt(index); + value &= ARMV7_EVTYPE_MASK; + __asm__ volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (value)); +} + +STATIC INLINE VOID Armv7EnableCnt(UINT32 index) +{ + UINT32 counter = ARMV7_IDX2CNT(index); + PRINT_DEBUG("index : %u, counter: %u\n", index, counter); + __asm__ volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (ARMV7_CNT2BIT(counter))); +} + +STATIC INLINE VOID Armv7DisableCnt(UINT32 index) +{ + UINT32 counter = ARMV7_IDX2CNT(index); + PRINT_DEBUG("index : %u, counter: %u\n", index, counter); + __asm__ volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (ARMV7_CNT2BIT(counter))); +} + +STATIC INLINE VOID Armv7EnableCntInterrupt(UINT32 index) +{ + UINT32 counter = ARMV7_IDX2CNT(index); + __asm__ volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (ARMV7_CNT2BIT(counter))); + ISB; +} + +STATIC INLINE VOID Armv7DisableCntInterrupt(UINT32 index) +{ + UINT32 counter = ARMV7_IDX2CNT(index); + __asm__ volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (ARMV7_CNT2BIT(counter))); + /* Clear the overflow flag in case an interrupt is pending. */ + __asm__ volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (ARMV7_CNT2BIT(counter))); + ISB; +} + +STATIC INLINE UINT32 Armv7PmuGetOverflowStatus(VOID) +{ + UINT32 value; + + __asm__ volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (value)); + value &= ARMV7_FLAG_MASK; + __asm__ volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (value)); + + return value; +} + +STATIC VOID Armv7EnableEvent(Event *event) +{ + UINT32 cnt = event->counter; + + if (!Armv7CntValid(cnt)) { + PRINT_ERR("CPU enabling wrong PMNC counter IRQ enable %u\n", cnt); + return; + } + + if (event->period == 0) { + PRINT_INFO("event period value not valid, counter: %u\n", cnt); + return; + } + /* + * Enable counter and interrupt, and set the counter to count + * the event that we're interested in. + */ + UINT32 intSave = LOS_IntLock(); + + Armv7DisableCnt(cnt); + + /* + * Set event (if destined for PMNx counters) + * We only need to set the event for the cycle counter if we + * have the ability to perform event filtering. + */ + if (cnt != ARMV7_IDX_CYCLE_COUNTER) { + Armv7BindEvt2Cnt(cnt, event->eventId); + } + + /* Enable interrupt for this counter */ + Armv7EnableCntInterrupt(cnt); + Armv7EnableCnt(cnt); + LOS_IntRestore(intSave); + + PRINT_DEBUG("enabled event: %u cnt: %u\n", event->eventId, cnt); +} + +STATIC VOID Armv7DisableEvent(Event *event) +{ + UINT32 cnt = event->counter; + + if (!Armv7CntValid(cnt)) { + PRINT_ERR("CPU enabling wrong PMNC counter IRQ enable %u\n", cnt); + return; + } + + UINT32 intSave = LOS_IntLock(); + Armv7DisableCnt(cnt); + Armv7DisableCntInterrupt(cnt); + LOS_IntRestore(intSave); +} + + +STATIC VOID Armv7StartAllCnt(VOID) +{ + PRINT_DEBUG("starting pmu...\n"); + + /* Enable all counters */ + UINT32 reg = Armv7PmncRead() | ARMV7_PMNC_E; + if (g_armv7Pmu.cntDivided) { + reg |= ARMV7_PMNC_D; + } else { + reg &= ~ARMV7_PMNC_D; + } + + Armv7PmncWrite(reg); + HalIrqUnmask(g_pmuIrqNr[ArchCurrCpuid()]); +} + +STATIC VOID Armv7StopAllCnt(VOID) +{ + PRINT_DEBUG("stopping pmu...\n"); + /* Disable all counters */ + Armv7PmncWrite(Armv7PmncRead() & ~ARMV7_PMNC_E); + + HalIrqMask(g_pmuIrqNr[ArchCurrCpuid()]); +} + +STATIC VOID Armv7ResetAllCnt(VOID) +{ + UINT32 index; + + /* The counter and interrupt enable registers are unknown at reset. */ + for (index = ARMV7_IDX_CYCLE_COUNTER; index < ARMV7_IDX_MAX_COUNTER; index++) { + Armv7DisableCnt(index); + Armv7DisableCntInterrupt(index); + } + + /* Initialize & Reset PMNC: C and P bits and D bits */ + UINT32 reg = ARMV7_PMNC_P | ARMV7_PMNC_C | (g_armv7Pmu.cntDivided ? ARMV7_PMNC_D : 0); + Armv7PmncWrite(reg); +} + +STATIC VOID Armv7SetEventPeriod(Event *event) +{ + if (event->period != 0) { + PRINT_INFO("counter: %u, period: 0x%x\n", event->counter, event->period); + Armv7PmuSetCntPeriod(event->counter, PERIOD_CALC(event->period)); + } +} + +STATIC UINTPTR Armv7ReadEventCnt(Event *event) +{ + UINT32 value = 0; + UINT32 index = event->counter; + + if (!Armv7CntValid(index)) { + PRINT_ERR("CPU reading wrong counter %u\n", index); + } else if (index == ARMV7_IDX_CYCLE_COUNTER) { + __asm__ volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); + } else { + Armv7PmuSelCnt(index); + __asm__ volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value)); + } + + if (value < PERIOD_CALC(event->period)) { + if (Armv7PmuCntOverflowed(Armv7PmuGetOverflowStatus(), event->counter)) { + value += event->period; + } + } else { + value -= PERIOD_CALC(event->period); + } + return value; +} + +STATIC const UINT32 g_armv7Map[] = { + [PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERF_HW_CYCLES, + [PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERF_HW_INSTRUCTIONS, + [PERF_COUNT_HW_DCACHE_REFERENCES] = ARMV7_PERF_HW_DCACHES, + [PERF_COUNT_HW_DCACHE_MISSES] = ARMV7_PERF_HW_DCACHE_MISSES, + [PERF_COUNT_HW_ICACHE_REFERENCES] = ARMV7_PERF_HW_ICACHES, + [PERF_COUNT_HW_ICACHE_MISSES] = ARMV7_PERF_HW_ICACHE_MISSES, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERF_HW_BRANCHES, + [PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERF_HW_BRANCE_MISSES, +}; + +UINT32 Armv7PmuMapEvent(UINT32 eventType, BOOL reverse) +{ + if (!reverse) { /* Common event to armv7 real event */ + if (eventType < ARRAY_SIZE(g_armv7Map)) { + return g_armv7Map[eventType]; + } + return eventType; + } else { /* Armv7 real event to common event */ + UINT32 i; + for (i = 0; i < ARRAY_SIZE(g_armv7Map); i++) { + if (g_armv7Map[i] == eventType) { + return i; + } + } + return PERF_HW_INVALID_EVENT_TYPE; + } +} + +STATIC VOID Armv7PmuIrqHandler(VOID) +{ + UINT32 index; + PerfRegs regs; + + PerfEvent *events = &(g_armv7Pmu.pmu.events); + UINT32 eventNum = events->nr; + + /* Get and reset the IRQ flags */ + UINT32 pmnc = Armv7PmuGetOverflowStatus(); + if (!Armv7PmuOverflowed(pmnc)) { + return; + } + + (VOID)memset_s(®s, sizeof(PerfRegs), 0, sizeof(PerfRegs)); + OsPerfFetchIrqRegs(®s); + + Armv7StopAllCnt(); + + for (index = 0; index < eventNum; index++) { + Event *event = &(events->per[index]); + /* + * We have a single interrupt for all counters. Check that + * each counter has overflowed before we process it. + */ + if (!Armv7PmuCntOverflowed(pmnc, event->counter) || (event->period == 0)) { + continue; + } + + Armv7PmuSetCntPeriod(event->counter, PERIOD_CALC(event->period)); + + OsPerfUpdateEventCount(event, event->period); + OsPerfHandleOverFlow(event, ®s); + } + Armv7StartAllCnt(); +} + +UINT32 OsGetPmuMaxCounter(VOID) +{ + return ARMV7_IDX_MAX_COUNTER; +} + +UINT32 OsGetPmuCycleCounter(VOID) +{ + return ARMV7_IDX_CYCLE_COUNTER; +} + +UINT32 OsGetPmuCounter0(VOID) +{ + return ARMV7_IDX_COUNTER0; +} + +STATIC HwPmu g_armv7Pmu = { + .canDivided = TRUE, + .enable = Armv7EnableEvent, + .disable = Armv7DisableEvent, + .start = Armv7StartAllCnt, + .stop = Armv7StopAllCnt, + .clear = Armv7ResetAllCnt, + .setPeriod = Armv7SetEventPeriod, + .readCnt = Armv7ReadEventCnt, + .mapEvent = Armv7PmuMapEvent, +}; + +UINT32 OsHwPmuInit(VOID) +{ + UINT32 ret; + UINT32 index; + + for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) { + ret = LOS_HwiCreate(g_pmuIrqNr[index], 0, 0, Armv7PmuIrqHandler, 0); + if (ret != LOS_OK) { + PRINT_ERR("pmu %u irq handler register failed\n", g_pmuIrqNr[index]); + return ret; + } +#ifdef LOSCFG_KERNEL_SMP + HalIrqSetAffinity(g_pmuIrqNr[index], CPUID_TO_AFFI_MASK(index)); +#endif + } + ret = OsPerfHwInit(&g_armv7Pmu); + return ret; +} diff --git a/arch/arm/gic/gic_v2.c b/arch/arm/gic/gic_v2.c index 9479c0d4a769a4c36479949a9bc1cb9426962831..e415c8e89e5b888e9d182fd0f7537bea89862699 100644 --- a/arch/arm/gic/gic_v2.c +++ b/arch/arm/gic/gic_v2.c @@ -149,6 +149,9 @@ VOID HalIrqInit(VOID) (VOID)LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0); (VOID)LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0); (VOID)LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpHaltHandler, 0); +#ifdef LOSCFG_KERNEL_SMP_CALL + (VOID)LOS_HwiCreate(LOS_MP_IPI_FUNC_CALL, 0xa0, 0, OsMpFuncCallHandler, 0); +#endif #endif } diff --git a/arch/arm/gic/gic_v3.c b/arch/arm/gic/gic_v3.c index f6e943f9593704a5473aaba85531b3eff68dcf90..a10d2b8c4e92afeb3a1949c21f0f9e1546ccb882 100644 --- a/arch/arm/gic/gic_v3.c +++ b/arch/arm/gic/gic_v3.c @@ -401,9 +401,12 @@ VOID HalIrqInit(VOID) #ifdef LOSCFG_KERNEL_SMP /* register inter-processor interrupt */ - LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0); - LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0); - LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpScheduleHandler, 0); + (VOID)LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0); + (VOID)LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0); + (VOID)LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpScheduleHandler, 0); +#ifdef LOSCFG_KERNEL_SMP_CALL + (VOID)LOS_HwiCreate(LOS_MP_IPI_FUNC_CALL, 0xa0, 0, OsMpFuncCallHandler, 0); +#endif #endif } diff --git a/arch/arm/include/perf.h b/arch/arm/include/perf.h new file mode 100644 index 0000000000000000000000000000000000000000..cb9ecd4d5554f61971fa89b5b9fc4131c9da1620 --- /dev/null +++ b/arch/arm/include/perf.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef _PERF_H +#define _PERF_H + +#include "los_typedef.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#define OsPerfArchFetchCallerRegs(regs) \ + do { \ + (regs)->pc = (UINTPTR)__builtin_return_address(0); \ + (regs)->fp = (UINTPTR)__builtin_frame_address(0); \ + } while (0) + +#define OsPerfArchFetchIrqRegs(regs, tcb) \ + do { \ + (regs)->pc = (tcb)->pc; \ + (regs)->fp = (tcb)->fp; \ + } while (0) + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif /* _PERF_H */ diff --git a/drivers/BUILD.gn b/drivers/BUILD.gn index b723778b6c8f08ab04a2982693ee553032748c59..ae8b7b451617760919495efb257c9f621ec43530 100644 --- a/drivers/BUILD.gn +++ b/drivers/BUILD.gn @@ -34,6 +34,7 @@ group("drivers") { "block/disk", "char/bch", "char/mem", + "char/perf", "char/quickstart", "char/random", "char/trace", diff --git a/drivers/Kconfig b/drivers/Kconfig index 9810a8e673ba26bf3af6654f6895b578bfcb3244..c603f8a96afe168ba7c559caa6f8d25c88a990a7 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -15,6 +15,7 @@ source "drivers/char/quickstart/Kconfig" source "drivers/char/random/Kconfig" source "drivers/char/video/Kconfig" source "drivers/char/trace/Kconfig" +source "drivers/char/perf/Kconfig" source "../../drivers/liteos/tzdriver/Kconfig" source "../../drivers/liteos/hievent/Kconfig" diff --git a/drivers/char/perf/BUILD.gn b/drivers/char/perf/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..223bc274b870dbfc7d95ab7c15106ebdc27f1aa7 --- /dev/null +++ b/drivers/char/perf/BUILD.gn @@ -0,0 +1,42 @@ +# 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. + +import("//kernel/liteos_a/liteos.gni") + +module_switch = defined(LOSCFG_DRIVERS_PERF) +module_name = "perf_dev" +kernel_module(module_name) { + sources = [ "src/perf.c" ] + + public_configs = [ ":public" ] +} + +config("public") { + include_dirs = [ "include" ] +} diff --git a/drivers/char/perf/Kconfig b/drivers/char/perf/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..b760bb57abdaac45db3ec47a4d15d6a118a48d80 --- /dev/null +++ b/drivers/char/perf/Kconfig @@ -0,0 +1,6 @@ +config DRIVERS_PERF + bool "Enable PERF DRIVER" + default y + depends on DRIVERS && FS_VFS && KERNEL_PERF + help + Answer Y to enable LiteOS support perf in userspace. diff --git a/drivers/char/perf/Makefile b/drivers/char/perf/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f3b7fe206a068698510215a425b4e4e59777e06c --- /dev/null +++ b/drivers/char/perf/Makefile @@ -0,0 +1,36 @@ +# 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 $(LITEOSTOPDIR)/config.mk + +MODULE_NAME := perf_dev + +LOCAL_SRCS := $(wildcard src/*.c) + +include $(MODULE) diff --git a/drivers/char/perf/include/los_dev_perf.h b/drivers/char/perf/include/los_dev_perf.h new file mode 100644 index 0000000000000000000000000000000000000000..414f12114cfe0361d9dd9b5def351aa1ca87e978 --- /dev/null +++ b/drivers/char/perf/include/los_dev_perf.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef __LOS_DEV_PERF_H__ +#define __LOS_DEV_PERF_H__ + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +int DevPerfRegister(void); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif diff --git a/drivers/char/perf/src/perf.c b/drivers/char/perf/src/perf.c new file mode 100644 index 0000000000000000000000000000000000000000..b5a292873e84307dc76cdadb50d940f69310805c --- /dev/null +++ b/drivers/char/perf/src/perf.c @@ -0,0 +1,153 @@ +/* + * 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 "fcntl.h" +#include "user_copy.h" +#include "sys/ioctl.h" +#include "fs/driver.h" +#include "los_dev_perf.h" +#include "los_perf.h" +#include "los_init.h" + +#define PERF_DRIVER "/dev/perf" +#define PERF_DRIVER_MODE 0666 + +/* perf ioctl */ +#define PERF_IOC_MAGIC 'T' +#define PERF_START _IO(PERF_IOC_MAGIC, 1) +#define PERF_STOP _IO(PERF_IOC_MAGIC, 2) + +static int PerfOpen(struct file *filep) +{ + (void)filep; + return 0; +} + +static int PerfClose(struct file *filep) +{ + (void)filep; + return 0; +} + +static ssize_t PerfRead(struct file *filep, char *buffer, size_t buflen) +{ + /* perf record buffer read */ + (void)filep; + int ret; + int realLen; + + char *records = LOS_MemAlloc(m_aucSysMem0, buflen); + if (records == NULL) { + return -ENOMEM; + } + + realLen = LOS_PerfDataRead(records, buflen); /* get sample data */ + if (realLen == 0) { + PRINT_ERR("Perf read failed, check whether perf is configured to sample mode.\n"); + ret = -EINVAL; + goto EXIT; + } + + ret = LOS_CopyFromKernel((void *)buffer, buflen, (void *)records, realLen); + if (ret != 0) { + ret = -EINVAL; + goto EXIT; + } + + ret = realLen; +EXIT: + LOS_MemFree(m_aucSysMem0, records); + return ret; +} + +static ssize_t PerfConfig(struct file *filep, const char *buffer, size_t buflen) +{ + (void)filep; + int ret; + PerfConfigAttr attr = {0}; + int attrlen = sizeof(PerfConfigAttr); + + if (buflen != attrlen) { + PRINT_ERR("PerfConfigAttr is %d bytes not %d\n", attrlen, buflen); + return -EINVAL; + } + + ret = LOS_CopyToKernel(&attr, attrlen, buffer, buflen); + if (ret != 0) { + return -EINVAL; + } + + ret = LOS_PerfConfig(&attr); + if (ret != LOS_OK) { + PRINT_ERR("perf config error %u\n", ret); + return -EINVAL; + } + + return 0; +} + +static int PerfIoctl(struct file *filep, int cmd, unsigned long arg) +{ + (void)filep; + switch (cmd) { + case PERF_START: + LOS_PerfStart((UINT32)arg); + break; + case PERF_STOP: + LOS_PerfStop(); + break; + default: + PRINT_ERR("Unknown perf ioctl cmd:%d\n", cmd); + return -EINVAL; + } + return 0; +} + +static const struct file_operations_vfs g_perfDevOps = { + PerfOpen, /* open */ + PerfClose, /* close */ + PerfRead, /* read */ + PerfConfig, /* write */ + NULL, /* seek */ + PerfIoctl, /* ioctl */ + NULL, /* mmap */ +#ifndef CONFIG_DISABLE_POLL + NULL, /* poll */ +#endif + NULL, /* unlink */ +}; + +int DevPerfRegister(void) +{ + return register_driver(PERF_DRIVER, &g_perfDevOps, PERF_DRIVER_MODE, 0); /* 0666: file mode */ +} + +LOS_MODULE_INIT(DevPerfRegister, LOS_INIT_LEVEL_KMOD_EXTENDED); diff --git a/kernel/Kconfig b/kernel/Kconfig index c556459798fb29975d6c331949b2dee2d7ca26a0..d7ae388c579214d88e8cfbc84cb5516df5941d36 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -26,6 +26,13 @@ config KERNEL_SMP_TASK_SYNC help This option will enable task synchronized operate task across cores. +config KERNEL_SMP_CALL + bool "Enable Function call cross Multi-core" + default n + depends on KERNEL_SMP + help + This option will enable function call on multi-core. + config KERNEL_SCHED_STATISTICS bool "Enable Scheduler statistics" default n diff --git a/kernel/base/include/los_percpu_pri.h b/kernel/base/include/los_percpu_pri.h index 1f5aa82827ca83f69b66044c77820aeaa2195eb8..c4bdd2b6506ba5cc93680b68b127042bd16dce0a 100644 --- a/kernel/base/include/los_percpu_pri.h +++ b/kernel/base/include/los_percpu_pri.h @@ -69,6 +69,9 @@ typedef struct { UINT32 schedFlag; /* pending scheduler flag */ #ifdef LOSCFG_KERNEL_SMP UINT32 excFlag; /* cpu halt or exc flag */ +#ifdef LOSCFG_KERNEL_SMP_CALL + LOS_DL_LIST funcLink; /* mp function call link */ +#endif #endif } Percpu; diff --git a/kernel/base/include/los_task_pri.h b/kernel/base/include/los_task_pri.h index b907004bef1efd8adcf1589a82cb6b7aa5e879d1..f35a86c1d0d85e68f74f5924857c7d1aa85f8b09 100644 --- a/kernel/base/include/los_task_pri.h +++ b/kernel/base/include/los_task_pri.h @@ -371,6 +371,10 @@ typedef struct { LOS_DL_LIST msgListHead; BOOL accessMap[LOSCFG_BASE_CORE_TSK_LIMIT]; #endif +#ifdef LOSCFG_KERNEL_PERF + UINTPTR pc; + UINTPTR fp; +#endif } LosTaskCB; typedef struct { diff --git a/kernel/base/mp/los_mp.c b/kernel/base/mp/los_mp.c index a465d52a352190bfcc0e48660205166c3758324c..dbbf9415122bc83aeaacf4f831b04566c05973cc 100644 --- a/kernel/base/mp/los_mp.c +++ b/kernel/base/mp/los_mp.c @@ -38,6 +38,12 @@ #ifdef LOSCFG_KERNEL_SMP +#ifdef LOSCFG_KERNEL_SMP_CALL +LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_mpCallSpin); +#define MP_CALL_LOCK(state) LOS_SpinLockSave(&g_mpCallSpin, &(state)) +#define MP_CALL_UNLOCK(state) LOS_SpinUnlockRestore(&g_mpCallSpin, (state)) +#endif + VOID LOS_MpSchedule(UINT32 target) { UINT32 cpuid = ArchCurrCpuid(); @@ -94,6 +100,70 @@ VOID OsMpCollectTasks(VOID) } } +#ifdef LOSCFG_KERNEL_SMP_CALL +VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args) +{ + UINT32 index; + UINT32 intSave; + + if (func == NULL) { + return; + } + + if (!(target & OS_MP_CPU_ALL)) { + return; + } + + for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) { + if (CPUID_TO_AFFI_MASK(index) & target) { + MpCallFunc *mpCallFunc = (MpCallFunc *)LOS_MemAlloc(m_aucSysMem0, sizeof(MpCallFunc)); + if (mpCallFunc == NULL) { + PRINT_ERR("smp func call malloc failed\n"); + return; + } + mpCallFunc->func = func; + mpCallFunc->args = args; + + MP_CALL_LOCK(intSave); + LOS_ListAdd(&g_percpu[index].funcLink, &(mpCallFunc->node)); + MP_CALL_UNLOCK(intSave); + } + } + HalIrqSendIpi(target, LOS_MP_IPI_FUNC_CALL); +} + +VOID OsMpFuncCallHandler(VOID) +{ + UINT32 intSave; + UINT32 cpuid = ArchCurrCpuid(); + LOS_DL_LIST *list = NULL; + MpCallFunc *mpCallFunc = NULL; + + MP_CALL_LOCK(intSave); + while (!LOS_ListEmpty(&g_percpu[cpuid].funcLink)) { + list = LOS_DL_LIST_FIRST(&g_percpu[cpuid].funcLink); + LOS_ListDelete(list); + MP_CALL_UNLOCK(intSave); + + mpCallFunc = LOS_DL_LIST_ENTRY(list, MpCallFunc, node); + mpCallFunc->func(mpCallFunc->args); + (VOID)LOS_MemFree(m_aucSysMem0, mpCallFunc); + + MP_CALL_LOCK(intSave); + } + MP_CALL_UNLOCK(intSave); +} + +VOID OsMpFuncCallInit(VOID) +{ + UINT32 index; + /* init funclink for each core */ + for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) { + LOS_ListInit(&g_percpu[index].funcLink); + } +} +#endif /* LOSCFG_KERNEL_SMP_CALL */ + UINT32 OsMpInit(VOID) { UINT16 swtmrId; @@ -101,7 +171,9 @@ UINT32 OsMpInit(VOID) (VOID)LOS_SwtmrCreate(OS_MP_GC_PERIOD, LOS_SWTMR_MODE_PERIOD, (SWTMR_PROC_FUNC)OsMpCollectTasks, &swtmrId, 0); (VOID)LOS_SwtmrStart(swtmrId); - +#ifdef LOSCFG_KERNEL_SMP_CALL + OsMpFuncCallInit(); +#endif return LOS_OK; } diff --git a/kernel/extended/BUILD.gn b/kernel/extended/BUILD.gn index a05c9097f068a4d09fdd85812e8b8241e5c57b56..e33f36f9766a73d5f11c46dc76f2a9521c3ec0b9 100644 --- a/kernel/extended/BUILD.gn +++ b/kernel/extended/BUILD.gn @@ -39,6 +39,7 @@ group("extended") { "hilog", "hook", "liteipc", + "perf", "pipes", "power", "trace", @@ -57,5 +58,6 @@ config("public") { "liteipc:public", "pipes:public", "vdso:public", + "perf:public", ] } diff --git a/kernel/extended/Kconfig b/kernel/extended/Kconfig index 2bd98c90b2ff27a7a5056212329ff21708af26a0..c826d3bc3ca485c6583b5a6713f51578be12a064 100644 --- a/kernel/extended/Kconfig +++ b/kernel/extended/Kconfig @@ -96,3 +96,6 @@ source "kernel/extended/blackbox/Kconfig" ######################### config options of hidumper ######################### source "kernel/extended/hidumper/Kconfig" + +######################### config options of perf ######################### +source "kernel/extended/perf/Kconfig" \ No newline at end of file diff --git a/kernel/extended/perf/BUILD.gn b/kernel/extended/perf/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..d5011834bc153570de0a9971d0bce7ec3be09c4a --- /dev/null +++ b/kernel/extended/perf/BUILD.gn @@ -0,0 +1,56 @@ +# 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. + +import("//kernel/liteos_a/liteos.gni") + +module_switch = defined(LOSCFG_KERNEL_PERF) +module_name = get_path_info(rebase_path("."), "name") +kernel_module(module_name) { + sources = [ + "los_perf.c", + "perf_output.c", + "perf_pmu.c", + ] + + if (defined(LOSCFG_PERF_HW_PMU)) { + sources += [ "pmu/perf_hw_pmu.c" ] + } + + if (defined(LOSCFG_PERF_TIMED_PMU)) { + sources += [ "pmu/perf_timed_pmu.c" ] + } + + if (defined(LOSCFG_PERF_SW_PMU)) { + sources += [ "pmu/perf_sw_pmu.c" ] + } +} + +config("public") { + include_dirs = [ "." ] +} diff --git a/kernel/extended/perf/Kconfig b/kernel/extended/perf/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..2860f078a844676922e73a7b6bbf6eec0c4ad309 --- /dev/null +++ b/kernel/extended/perf/Kconfig @@ -0,0 +1,38 @@ +config KERNEL_PERF + bool "Enable Perf Feature" + default n + depends on KERNEL_EXTKERNEL + select KERNEL_SMP_CALL if KERNEL_SMP + help + If you wish to build LiteOS with support for perf. + +choice + prompt "Time-consuming Calc Methods" + depends on KERNEL_PERF + +config PERF_CALC_TIME_BY_TICK + bool "By Tick" + +config PERF_CALC_TIME_BY_CYCLE + bool "By Cpu Cycle" +endchoice + +config PERF_BUFFER_SIZE + int "Perf Sampling Buffer Size" + default 20480 + depends on KERNEL_PERF + +config PERF_HW_PMU + bool "Enable Hardware Pmu Events for Sampling" + default n + depends on KERNEL_PERF + +config PERF_TIMED_PMU + bool "Enable Hrtimer Period Events for Sampling" + default n + depends on KERNEL_PERF && HRTIMER_ENABLE + +config PERF_SW_PMU + bool "Enable Software Events for Sampling" + default y + depends on KERNEL_PERF && KERNEL_HOOK \ No newline at end of file diff --git a/kernel/extended/perf/Makefile b/kernel/extended/perf/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a7bce008fffea57437485322b85d54cb0783e90a --- /dev/null +++ b/kernel/extended/perf/Makefile @@ -0,0 +1,22 @@ +include $(LITEOSTOPDIR)/config.mk + +MODULE_NAME := $(notdir $(shell pwd)) + +LOCAL_SRCS := $(wildcard *.c) + +ifeq ($(LOSCFG_PERF_HW_PMU), y) +LOCAL_SRCS += $(wildcard pmu/perf_hw_pmu.c) +endif + +ifeq ($(LOSCFG_PERF_TIMED_PMU), y) +LOCAL_SRCS += $(wildcard pmu/perf_timed_pmu.c) +endif + +ifeq ($(LOSCFG_PERF_SW_PMU), y) +LOCAL_SRCS += $(wildcard pmu/perf_sw_pmu.c) +endif + +LOCAL_FLAGS := $(LOCAL_INCLUDE) + +include $(MODULE) + diff --git a/kernel/extended/perf/los_perf.c b/kernel/extended/perf/los_perf.c new file mode 100644 index 0000000000000000000000000000000000000000..2223b802c2e3f6f9bc9522c12165c332069468e9 --- /dev/null +++ b/kernel/extended/perf/los_perf.c @@ -0,0 +1,544 @@ +/* + * 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); diff --git a/kernel/extended/perf/los_perf_pri.h b/kernel/extended/perf/los_perf_pri.h new file mode 100644 index 0000000000000000000000000000000000000000..a57bd81f97da0b40d473f8abb837c438f6187433 --- /dev/null +++ b/kernel/extended/perf/los_perf_pri.h @@ -0,0 +1,158 @@ +/* + * 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. + */ + +#ifndef _LOS_PERF_PRI_H +#define _LOS_PERF_PRI_H + +#include "los_perf.h" +#include "perf.h" +#include "los_mp.h" +#include "los_task_pri.h" +#include "los_exc_pri.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#define PERF_EVENT_TO_CODE 0 +#define PERF_CODE_TO_EVENT 1 +#define PERF_DATA_MAGIC_WORD 0xEFEFEF00 + +#define SMP_CALL_PERF_FUNC(func) OsMpFuncCall(OS_MP_CPU_ALL, (SMP_FUNC_CALL)func, NULL) + +enum PmuStatus { + PERF_PMU_STOPED, + PERF_PMU_STARTED, +}; + +typedef struct { + UINTPTR pc; + UINTPTR fp; +} PerfRegs; + +typedef struct { + UINT32 ipNr; + IpInfo ip[PERF_MAX_CALLCHAIN_DEPTH]; +} PerfBackTrace; + +typedef struct { + UINT32 cpuid; /* cpu id */ + UINT32 taskId; /* task id */ + UINT32 processId; /* process id */ + UINT32 eventId; /* record type */ + UINT32 period; /* record period */ + UINT64 time; /* record time */ + IpInfo pc; /* instruction pointer */ + PerfBackTrace callChain; /* number of callChain ips */ +} PerfSampleData; + +typedef struct { + UINT32 magic; /* magic number */ + UINT32 eventType; /* enum PerfEventType */ + UINT32 len; /* sample data file length */ + UINT32 sampleType; /* IP | TID | TIMESTAMP... */ + UINT32 sectionId; /* section id */ +} PerfDataHdr; + +typedef struct { + UINT32 counter; + UINT32 eventId; + UINT32 period; + UINT64 count[LOSCFG_KERNEL_CORE_NUM]; +} Event; + +typedef struct { + Event per[PERF_MAX_EVENT]; + UINT8 nr; + UINT8 cntDivided; +} PerfEvent; + +typedef struct { + enum PerfEventType type; + PerfEvent events; + UINT32 (*config)(VOID); + UINT32 (*start)(VOID); + UINT32 (*stop)(VOID); + CHAR *(*getName)(Event *event); +} Pmu; + +typedef struct { + /* time info */ + UINT64 startTime; + UINT64 endTime; + + /* instrumentation status */ + enum PerfStatus status; + enum PmuStatus pmuStatusPerCpu[LOSCFG_KERNEL_CORE_NUM]; + + /* configuration info */ + UINT32 sampleType; + UINT32 taskIds[PERF_MAX_FILTER_TSKS]; + UINT8 taskIdsNr; + UINT32 processIds[PERF_MAX_FILTER_TSKS]; + UINT8 processIdsNr; + UINT8 needSample; +} PerfCB; + +#ifndef OsPerfArchFetchIrqRegs +STATIC INLINE VOID OsPerfArchFetchIrqRegs(PerfRegs *regs, LosTaskCB *curTask) {} +#endif + +STATIC INLINE VOID OsPerfFetchIrqRegs(PerfRegs *regs) +{ + LosTaskCB *curTask = OsCurrTaskGet(); + OsPerfArchFetchIrqRegs(regs, curTask); + PRINT_DEBUG("pc = 0x%x, fp = 0x%x\n", regs->pc, regs->fp); +} + +#ifndef OsPerfArchFetchCallerRegs +STATIC INLINE VOID OsPerfArchFetchCallerRegs(PerfRegs *regs) {} +#endif + +STATIC INLINE VOID OsPerfFetchCallerRegs(PerfRegs *regs) +{ + OsPerfArchFetchCallerRegs(regs); + PRINT_DEBUG("pc = 0x%x, fp = 0x%x\n", regs->pc, regs->fp); +} + +extern VOID OsPerfSetIrqRegs(UINTPTR pc, UINTPTR fp); +extern VOID OsPerfUpdateEventCount(Event *event, UINT32 value); +extern VOID OsPerfHandleOverFlow(Event *event, PerfRegs *regs); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif /* _LOS_PERF_PRI_H */ diff --git a/kernel/extended/perf/perf_output.c b/kernel/extended/perf/perf_output.c new file mode 100644 index 0000000000000000000000000000000000000000..e9e9ca10284eadb0ed0fe837f354b83127d1137d --- /dev/null +++ b/kernel/extended/perf/perf_output.c @@ -0,0 +1,126 @@ +/* + * 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 "perf_output_pri.h" + +STATIC PERF_BUF_NOTIFY_HOOK g_perfBufNotifyHook = NULL; +STATIC PERF_BUF_FLUSH_HOOK g_perfBufFlushHook = NULL; +STATIC PerfOutputCB g_perfOutputCb; + +STATIC VOID OsPerfDefaultNotify(VOID) +{ + PRINT_INFO("perf buf waterline notify!\n"); +} + +UINT32 OsPerfOutputInit(VOID *buf, UINT32 size) +{ + UINT32 ret; + BOOL releaseFlag = FALSE; + if (buf == NULL) { + buf = LOS_MemAlloc(m_aucSysMem1, size); + if (buf == NULL) { + return LOS_NOK; + } else { + releaseFlag = TRUE; + } + } + ret = LOS_CirBufInit(&g_perfOutputCb.ringbuf, buf, size); + if (ret != LOS_OK) { + goto RELEASE; + } + g_perfOutputCb.waterMark = size / PERF_BUFFER_WATERMARK_ONE_N; + g_perfBufNotifyHook = OsPerfDefaultNotify; + return ret; +RELEASE: + if (releaseFlag) { + (VOID)LOS_MemFree(m_aucSysMem1, buf); + } + return ret; +} + +VOID OsPerfOutputFlush(VOID) +{ + if (g_perfBufFlushHook != NULL) { + g_perfBufFlushHook(g_perfOutputCb.ringbuf.fifo, g_perfOutputCb.ringbuf.size); + } +} + +UINT32 OsPerfOutputRead(CHAR *dest, UINT32 size) +{ + OsPerfOutputFlush(); + return LOS_CirBufRead(&g_perfOutputCb.ringbuf, dest, size); +} + +STATIC BOOL OsPerfOutputBegin(UINT32 size) +{ + if (g_perfOutputCb.ringbuf.remain < size) { + PRINT_INFO("perf buf has no enough space for 0x%x\n", size); + return FALSE; + } + return TRUE; +} + +STATIC VOID OsPerfOutputEnd(VOID) +{ + OsPerfOutputFlush(); + if (LOS_CirBufUsedSize(&g_perfOutputCb.ringbuf) >= g_perfOutputCb.waterMark) { + if (g_perfBufNotifyHook != NULL) { + g_perfBufNotifyHook(); + } + } +} + +UINT32 OsPerfOutputWrite(CHAR *data, UINT32 size) +{ + if (!OsPerfOutputBegin(size)) { + return LOS_NOK; + } + + LOS_CirBufWrite(&g_perfOutputCb.ringbuf, data, size); + + OsPerfOutputEnd(); + return LOS_OK; +} + +VOID OsPerfOutputInfo(VOID) +{ + PRINT_EMG("dump perf data, addr: %p length: %#x\n", g_perfOutputCb.ringbuf.fifo, g_perfOutputCb.ringbuf.size); +} + +VOID OsPerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func) +{ + g_perfBufNotifyHook = func; +} + +VOID OsPerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func) +{ + g_perfBufFlushHook = func; +} diff --git a/kernel/extended/perf/perf_output_pri.h b/kernel/extended/perf/perf_output_pri.h new file mode 100644 index 0000000000000000000000000000000000000000..c9c8c9c1bc94e60cbb1acc014e8b6709a6e3f779 --- /dev/null +++ b/kernel/extended/perf/perf_output_pri.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef _PERF_OUTPUT_PRI_H +#define _PERF_OUTPUT_PRI_H + +#include "los_perf_pri.h" +#include "los_cir_buf.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +typedef struct { + CirBuf ringbuf; /* ring buffer */ + UINT32 waterMark; /* notify water mark */ +} PerfOutputCB; + +extern UINT32 OsPerfOutputInit(VOID *buf, UINT32 size); +extern UINT32 OsPerfOutputRead(CHAR *dest, UINT32 size); +extern UINT32 OsPerfOutputWrite(CHAR *data, UINT32 size); +extern VOID OsPerfOutputInfo(VOID); +extern VOID OsPerfOutputFlush(VOID); +extern VOID OsPerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func); +extern VOID OsPerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif /* _PERF_OUTPUT_PRI_H */ diff --git a/kernel/extended/perf/perf_pmu.c b/kernel/extended/perf/perf_pmu.c new file mode 100644 index 0000000000000000000000000000000000000000..1ad5474618e4e87da753569b248622da514d07dc --- /dev/null +++ b/kernel/extended/perf/perf_pmu.c @@ -0,0 +1,70 @@ +/* + * 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 "perf_pmu_pri.h" + +STATIC Pmu *g_pmuMgr[PERF_EVENT_TYPE_MAX] = { NULL }; + +UINT32 OsPerfPmuRegister(Pmu *pmu) +{ + UINT32 type; + + if ((pmu == NULL) || (pmu->type >= PERF_EVENT_TYPE_MAX)) { + return LOS_NOK; + } + + type = pmu->type; + if (g_pmuMgr[type] == NULL) { + g_pmuMgr[type] = pmu; + return LOS_OK; + } + return LOS_NOK; +} + +Pmu *OsPerfPmuGet(UINT32 type) +{ + if (type >= PERF_EVENT_TYPE_MAX) { + return NULL; + } + + if (type == PERF_EVENT_TYPE_RAW) { /* process hardware raw events with hard pmu */ + type = PERF_EVENT_TYPE_HW; + } + return g_pmuMgr[type]; +} + +VOID OsPerfPmuRm(UINT32 type) +{ + if (type >= PERF_EVENT_TYPE_MAX) { + return; + } + g_pmuMgr[type] = NULL; +} diff --git a/kernel/extended/perf/perf_pmu_pri.h b/kernel/extended/perf/perf_pmu_pri.h new file mode 100644 index 0000000000000000000000000000000000000000..f1a1edc201de65ddd1de67f7da72c44776dd2f51 --- /dev/null +++ b/kernel/extended/perf/perf_pmu_pri.h @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#ifndef _PERF_PMU_PRI_H +#define _PERF_PMU_PRI_H + +#include "los_perf_pri.h" + +#ifdef LOSCFG_HRTIMER_ENABLE +#include "linux/hrtimer.h" +#endif + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +typedef struct { + Pmu pmu; + BOOL canDivided; + UINT32 cntDivided; + VOID (*enable)(Event *event); + VOID (*disable)(Event *event); + VOID (*start)(VOID); + VOID (*stop)(VOID); + VOID (*clear)(VOID); + VOID (*setPeriod)(Event *event); + UINTPTR (*readCnt)(Event *event); + UINT32 (*mapEvent)(UINT32 eventType, BOOL reverse); +} HwPmu; + +typedef struct { + Pmu pmu; + union { + struct { /* trace event */ + BOOL enable; + }; +#ifdef LOSCFG_HRTIMER_ENABLE + struct { /* timer event */ + struct hrtimer hrtimer; + union ktime time; + union ktime cfgTime; + }; +#endif + }; +} SwPmu; + +#define GET_HW_PMU(item) LOS_DL_LIST_ENTRY(item, HwPmu, pmu) + +#define TIMER_PERIOD_LOWER_BOUND_US 100 + +#define CCNT_FULL 0xFFFFFFFF +#define CCNT_PERIOD_LOWER_BOUND 0x00000000 +#define CCNT_PERIOD_UPPER_BOUND 0xFFFFFF00 +#define PERIOD_CALC(p) (CCNT_FULL - (p)) +#define VALID_PERIOD(p) ((PERIOD_CALC(p) > CCNT_PERIOD_LOWER_BOUND) \ + && (PERIOD_CALC(p) < CCNT_PERIOD_UPPER_BOUND)) + +#define PERF_HW_INVALID_EVENT_TYPE 0xFFFFFFFF + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +#define PMU_LABEL_INT_1 \ + NUM_HAL_INTERRUPT_PMU_0, +#define PMU_LABEL_INT_2 \ + PMU_LABEL_INT_1 \ + NUM_HAL_INTERRUPT_PMU_1, +#define PMU_LABEL_INT_3 \ + PMU_LABEL_INT_2 \ + NUM_HAL_INTERRUPT_PMU_2, +#define PMU_LABEL_INT_4 \ + PMU_LABEL_INT_3 \ + NUM_HAL_INTERRUPT_PMU_3, + +#define PMU_INT(_num) PMU_LABEL_INT_##_num + +#define OS_PMU_INTS(_num, _list) \ + STATIC UINT32 _list [_num] = { \ + PMU_INT(_num) \ + } + +extern UINT32 OsPerfPmuRegister(Pmu *pmu); +extern VOID OsPerfPmuRm(UINT32 type); +extern Pmu *OsPerfPmuGet(UINT32 type); + +extern UINT32 OsHwPmuInit(VOID); +extern UINT32 OsSwPmuInit(VOID); +extern UINT32 OsTimedPmuInit(VOID); + +extern UINT32 OsGetPmuCounter0(VOID); +extern UINT32 OsGetPmuMaxCounter(VOID); +extern UINT32 OsGetPmuCycleCounter(VOID); +extern UINT32 OsPerfHwInit(HwPmu *hwPmu); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif /* _PERF_PMU_PRI_H */ diff --git a/kernel/extended/perf/pmu/perf_hw_pmu.c b/kernel/extended/perf/pmu/perf_hw_pmu.c new file mode 100644 index 0000000000000000000000000000000000000000..0c5e9e1b1ea5a00430f3d9578fc538d139df81a5 --- /dev/null +++ b/kernel/extended/perf/pmu/perf_hw_pmu.c @@ -0,0 +1,187 @@ +/* + * 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 "perf_pmu_pri.h" + +STATIC Pmu *g_perfHw = NULL; + +STATIC CHAR *g_eventName[PERF_COUNT_HW_MAX] = { + [PERF_COUNT_HW_CPU_CYCLES] = "cycles", + [PERF_COUNT_HW_INSTRUCTIONS] = "instructions", + [PERF_COUNT_HW_ICACHE_REFERENCES] = "icache", + [PERF_COUNT_HW_ICACHE_MISSES] = "icache-misses", + [PERF_COUNT_HW_DCACHE_REFERENCES] = "dcache", + [PERF_COUNT_HW_DCACHE_MISSES] = "dcache-misses", + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = "branches", + [PERF_COUNT_HW_BRANCH_MISSES] = "branches-misses", +}; + +/** + * 1.If config event is PERF_EVENT_TYPE_HW, then map it to the real eventId first, otherwise use the configured + * eventId directly. + * 2.Find available counter for each event. + * 3.Decide whether this hardware pmu need prescaler (once every 64 cycle counts). + */ +STATIC UINT32 OsPerfHwConfig(VOID) +{ + UINT32 i; + HwPmu *armPmu = GET_HW_PMU(g_perfHw); + + UINT32 maxCounter = OsGetPmuMaxCounter(); + UINT32 counter = OsGetPmuCounter0(); + UINT32 cycleCounter = OsGetPmuCycleCounter(); + UINT32 cycleCode = armPmu->mapEvent(PERF_COUNT_HW_CPU_CYCLES, PERF_EVENT_TO_CODE); + if (cycleCode == PERF_HW_INVALID_EVENT_TYPE) { + return LOS_NOK; + } + + PerfEvent *events = &g_perfHw->events; + UINT32 eventNum = events->nr; + + for (i = 0; i < eventNum; i++) { + Event *event = &(events->per[i]); + + if (!VALID_PERIOD(event->period)) { + PRINT_ERR("Config period: 0x%x invalid, should be in (%#x, %#x)\n", event->period, + PERIOD_CALC(CCNT_PERIOD_UPPER_BOUND), PERIOD_CALC(CCNT_PERIOD_LOWER_BOUND)); + return LOS_NOK; + } + + if (g_perfHw->type == PERF_EVENT_TYPE_HW) { /* do map */ + UINT32 eventId = armPmu->mapEvent(event->eventId, PERF_EVENT_TO_CODE); + if (eventId == PERF_HW_INVALID_EVENT_TYPE) { + return LOS_NOK; + } + event->eventId = eventId; + } + + if (event->eventId == cycleCode) { + event->counter = cycleCounter; + } else { + event->counter = counter; + counter++; + } + + if (counter >= maxCounter) { + PRINT_ERR("max events: %u excluding cycle event\n", maxCounter - 1); + return LOS_NOK; + } + + PRINT_DEBUG("Perf Config %u eventId = 0x%x, counter = 0x%x, period = 0x%x\n", i, event->eventId, event->counter, + event->period); + } + + armPmu->cntDivided = events->cntDivided & armPmu->canDivided; + return LOS_OK; +} + +STATIC UINT32 OsPerfHwStart(VOID) +{ + UINT32 i; + UINT32 cpuid = ArchCurrCpuid(); + HwPmu *armPmu = GET_HW_PMU(g_perfHw); + + PerfEvent *events = &g_perfHw->events; + UINT32 eventNum = events->nr; + + armPmu->clear(); + + for (i = 0; i < eventNum; i++) { + Event *event = &(events->per[i]); + armPmu->setPeriod(event); + armPmu->enable(event); + event->count[cpuid] = 0; + } + + armPmu->start(); + return LOS_OK; +} + +STATIC UINT32 OsPerfHwStop(VOID) +{ + UINT32 i; + UINT32 cpuid = ArchCurrCpuid(); + HwPmu *armPmu = GET_HW_PMU(g_perfHw); + + PerfEvent *events = &g_perfHw->events; + UINT32 eventNum = events->nr; + + armPmu->stop(); + + for (i = 0; i < eventNum; i++) { + Event *event = &(events->per[i]); + UINTPTR value = armPmu->readCnt(event); + PRINT_DEBUG("perf stop readCnt value = 0x%x\n", value); + event->count[cpuid] += value; + + /* multiplier of cycle counter */ + UINT32 eventId = armPmu->mapEvent(event->eventId, PERF_CODE_TO_EVENT); + if ((eventId == PERF_COUNT_HW_CPU_CYCLES) && (armPmu->cntDivided != 0)) { + PRINT_DEBUG("perf stop is cycle\n"); + event->count[cpuid] = event->count[cpuid] << 6; /* CCNT counts every 64th cpu cycle */ + } + PRINT_DEBUG("perf stop eventCount[0x%x] : [%s] = %llu\n", event->eventId, g_eventName[eventId], + event->count[cpuid]); + } + return LOS_OK; +} + +STATIC CHAR *OsPerfGetEventName(Event *event) +{ + UINT32 eventId; + HwPmu *armPmu = GET_HW_PMU(g_perfHw); + eventId = armPmu->mapEvent(event->eventId, PERF_CODE_TO_EVENT); + if (eventId < PERF_COUNT_HW_MAX) { + return g_eventName[eventId]; + } else { + return "unknown"; + } +} + +UINT32 OsPerfHwInit(HwPmu *hwPmu) +{ + UINT32 ret; + if (hwPmu == NULL) { + return LOS_NOK; + } + + hwPmu->pmu.type = PERF_EVENT_TYPE_HW; + hwPmu->pmu.config = OsPerfHwConfig; + hwPmu->pmu.start = OsPerfHwStart; + hwPmu->pmu.stop = OsPerfHwStop; + hwPmu->pmu.getName = OsPerfGetEventName; + + (VOID)memset_s(&hwPmu->pmu.events, sizeof(PerfEvent), 0, sizeof(PerfEvent)); + ret = OsPerfPmuRegister(&hwPmu->pmu); + + g_perfHw = OsPerfPmuGet(PERF_EVENT_TYPE_HW); + return ret; +} diff --git a/kernel/extended/perf/pmu/perf_sw_pmu.c b/kernel/extended/perf/pmu/perf_sw_pmu.c new file mode 100644 index 0000000000000000000000000000000000000000..971a2b2623edac68675ee1915a032be3cb7d1d84 --- /dev/null +++ b/kernel/extended/perf/pmu/perf_sw_pmu.c @@ -0,0 +1,169 @@ +/* + * 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 "perf_pmu_pri.h" +#include "los_hook.h" + +STATIC SwPmu g_perfSw; +STATIC CHAR *g_eventName[PERF_COUNT_SW_MAX] = { + [PERF_COUNT_SW_TASK_SWITCH] = "task switch", + [PERF_COUNT_SW_IRQ_RESPONSE] = "irq response", + [PERF_COUNT_SW_MEM_ALLOC] = "mem alloc", + [PERF_COUNT_SW_MUX_PEND] = "mux pend", +}; + +STATIC UINT32 g_traceEventMap[PERF_COUNT_SW_MAX] = { + [PERF_COUNT_SW_TASK_SWITCH] = LOS_HOOK_TYPE_TASK_SWITCHEDIN, + [PERF_COUNT_SW_IRQ_RESPONSE] = LOS_HOOK_TYPE_ISR_ENTER, + [PERF_COUNT_SW_MEM_ALLOC] = LOS_HOOK_TYPE_MEM_ALLOC, + [PERF_COUNT_SW_MUX_PEND] = LOS_HOOK_TYPE_MUX_PEND, +}; + +VOID OsPerfHook(UINT32 eventType) +{ + if (!g_perfSw.enable) { + return; + } + + PerfEvent *events = &g_perfSw.pmu.events; + UINT32 eventNum = events->nr; + + UINT32 i; + PerfRegs regs; + + (VOID)memset_s(®s, sizeof(PerfRegs), 0, sizeof(PerfRegs)); + + for (i = 0; i < eventNum; i++) { + Event *event = &(events->per[i]); + if (event->counter == eventType) { + OsPerfUpdateEventCount(event, 1); + if (event->count[ArchCurrCpuid()] % event->period == 0) { + OsPerfFetchCallerRegs(®s); + OsPerfHandleOverFlow(event, ®s); + } + return; + } + } +} + +STATIC VOID LOS_PerfMemAlloc(VOID *pool, VOID *ptr, UINT32 size) +{ + OsPerfHook(LOS_HOOK_TYPE_MEM_ALLOC); +} + +STATIC VOID LOS_PerfMuxPend(const LosMux *muxCB, UINT32 timeout) +{ + OsPerfHook(LOS_HOOK_TYPE_MUX_PEND); +} + +STATIC VOID LOS_PerfIsrEnter(UINT32 hwiNum) +{ + OsPerfHook(LOS_HOOK_TYPE_ISR_ENTER); +} + +STATIC VOID LOS_PerfTaskSwitchedIn(const LosTaskCB *newTask, const LosTaskCB *runTask) +{ + OsPerfHook(LOS_HOOK_TYPE_TASK_SWITCHEDIN); +} + +STATIC VOID OsPerfCnvInit(VOID) +{ + LOS_HookReg(LOS_HOOK_TYPE_MEM_ALLOC, LOS_PerfMemAlloc); + LOS_HookReg(LOS_HOOK_TYPE_MUX_PEND, LOS_PerfMuxPend); + LOS_HookReg(LOS_HOOK_TYPE_ISR_ENTER, LOS_PerfIsrEnter); + LOS_HookReg(LOS_HOOK_TYPE_TASK_SWITCHEDIN, LOS_PerfTaskSwitchedIn); +} + +STATIC UINT32 OsPerfSwConfig(VOID) +{ + UINT32 i; + PerfEvent *events = &g_perfSw.pmu.events; + UINT32 eventNum = events->nr; + + for (i = 0; i < eventNum; i++) { + Event *event = &(events->per[i]); + if ((event->eventId < PERF_COUNT_SW_TASK_SWITCH) || (event->eventId >= PERF_COUNT_SW_MAX) || + (event->period == 0)) { + return LOS_NOK; + } + event->counter = g_traceEventMap[event->eventId]; + } + return LOS_OK; +} + +STATIC UINT32 OsPerfSwStart(VOID) +{ + UINT32 i; + UINT32 cpuid = ArchCurrCpuid(); + PerfEvent *events = &g_perfSw.pmu.events; + UINT32 eventNum = events->nr; + + for (i = 0; i < eventNum; i++) { + Event *event = &(events->per[i]); + event->count[cpuid] = 0; + } + + g_perfSw.enable = TRUE; + return LOS_OK; +} + +STATIC UINT32 OsPerfSwStop(VOID) +{ + g_perfSw.enable = FALSE; + return LOS_OK; +} + +STATIC CHAR *OsPerfGetEventName(Event *event) +{ + UINT32 eventId = event->eventId; + if (eventId < PERF_COUNT_SW_MAX) { + return g_eventName[eventId]; + } + return "unknown"; +} + +UINT32 OsSwPmuInit(VOID) +{ + g_perfSw.pmu = (Pmu) { + .type = PERF_EVENT_TYPE_SW, + .config = OsPerfSwConfig, + .start = OsPerfSwStart, + .stop = OsPerfSwStop, + .getName = OsPerfGetEventName, + }; + + g_perfSw.enable = FALSE; + + OsPerfCnvInit(); + + (VOID)memset_s(&g_perfSw.pmu.events, sizeof(PerfEvent), 0, sizeof(PerfEvent)); + return OsPerfPmuRegister(&g_perfSw.pmu); +} diff --git a/kernel/extended/perf/pmu/perf_timed_pmu.c b/kernel/extended/perf/pmu/perf_timed_pmu.c new file mode 100644 index 0000000000000000000000000000000000000000..abe640d91d196040e7814b64a46669404858f5fe --- /dev/null +++ b/kernel/extended/perf/pmu/perf_timed_pmu.c @@ -0,0 +1,177 @@ +/* + * 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 "perf_pmu_pri.h" + +#define US_PER_SECOND 1000000 +#define HRTIMER_DEFAULT_PERIOD_US 1000 + +STATIC SwPmu g_perfTimed; + +STATIC BOOL OsPerfTimedPeriodValid(UINT32 period) +{ + return period >= TIMER_PERIOD_LOWER_BOUND_US; +} + +STATIC UINT32 OsPerfTimedStart(VOID) +{ + UINT32 i; + UINT32 cpuid = ArchCurrCpuid(); + PerfEvent *events = &g_perfTimed.pmu.events; + UINT32 eventNum = events->nr; + + for (i = 0; i < eventNum; i++) { + Event *event = &(events->per[i]); + event->count[cpuid] = 0; + } + + if (cpuid != 0) { /* only need start on one core */ + return LOS_OK; + } + + if (hrtimer_start(&g_perfTimed.hrtimer, g_perfTimed.time, HRTIMER_MODE_REL) != 0) { + PRINT_ERR("Hrtimer start failed\n"); + return LOS_NOK; + } + + if (hrtimer_forward(&g_perfTimed.hrtimer, g_perfTimed.cfgTime) == 0) { + PRINT_ERR("Hrtimer forward failed\n"); + return LOS_NOK; + } + + g_perfTimed.time = g_perfTimed.cfgTime; + return LOS_OK; +} + +STATIC UINT32 OsPerfTimedConfig(VOID) +{ + UINT32 i; + PerfEvent *events = &g_perfTimed.pmu.events; + UINT32 eventNum = events->nr; + + for (i = 0; i < eventNum; i++) { + Event *event = &(events->per[i]); + UINT32 period = event->period; + if (event->eventId == PERF_COUNT_CPU_CLOCK) { + if (!OsPerfTimedPeriodValid(period)) { + period = TIMER_PERIOD_LOWER_BOUND_US; + PRINT_ERR("config period invalid, should be >= 100, use default period:%u us\n", period); + } + + g_perfTimed.cfgTime = (union ktime) { + .tv.sec = period / US_PER_SECOND, + .tv.usec = period % US_PER_SECOND + }; + PRINT_INFO("hrtimer config period - sec:%d, usec:%d\n", g_perfTimed.cfgTime.tv.sec, + g_perfTimed.cfgTime.tv.usec); + return LOS_OK; + } + } + return LOS_NOK; +} + +STATIC UINT32 OsPerfTimedStop(VOID) +{ + UINT32 ret; + + if (ArchCurrCpuid() != 0) { /* only need stop on one core */ + return LOS_OK; + } + + ret = hrtimer_cancel(&g_perfTimed.hrtimer); + if (ret != 1) { + PRINT_ERR("Hrtimer stop failed!, 0x%x\n", ret); + return LOS_NOK; + } + return LOS_OK; +} + +STATIC VOID OsPerfTimedHandle(VOID) +{ + UINT32 index; + PerfRegs regs; + + PerfEvent *events = &g_perfTimed.pmu.events; + UINT32 eventNum = events->nr; + + (VOID)memset_s(®s, sizeof(PerfRegs), 0, sizeof(PerfRegs)); + OsPerfFetchIrqRegs(®s); + + for (index = 0; index < eventNum; index++) { + Event *event = &(events->per[index]); + OsPerfUpdateEventCount(event, 1); /* eventCount += 1 every once */ + OsPerfHandleOverFlow(event, ®s); + } +} + +STATIC enum hrtimer_restart OsPerfHrtimer(struct hrtimer *hrtimer) +{ + SMP_CALL_PERF_FUNC(OsPerfTimedHandle); /* send to all cpu to collect data */ + return HRTIMER_RESTART; +} + +STATIC CHAR *OsPerfGetEventName(Event *event) +{ + if (event->eventId == PERF_COUNT_CPU_CLOCK) { + return "timed"; + } else { + return "unknown"; + } +} + +UINT32 OsTimedPmuInit(VOID) +{ + UINT32 ret; + + g_perfTimed.time = (union ktime) { + .tv.sec = 0, + .tv.usec = HRTIMER_DEFAULT_PERIOD_US, + }; + + hrtimer_init(&g_perfTimed.hrtimer, 1, HRTIMER_MODE_REL); + + ret = hrtimer_create(&g_perfTimed.hrtimer, g_perfTimed.time, OsPerfHrtimer); + if (ret != LOS_OK) { + return ret; + } + + g_perfTimed.pmu = (Pmu) { + .type = PERF_EVENT_TYPE_TIMED, + .config = OsPerfTimedConfig, + .start = OsPerfTimedStart, + .stop = OsPerfTimedStop, + .getName = OsPerfGetEventName, + }; + + (VOID)memset_s(&g_perfTimed.pmu.events, sizeof(PerfEvent), 0, sizeof(PerfEvent)); + ret = OsPerfPmuRegister(&g_perfTimed.pmu); + return ret; +} diff --git a/kernel/include/los_err.h b/kernel/include/los_err.h index 9d5bf4b7266dde055cd279d4616b2e22e332a670..eb64b7d95e65847ed4a89fda72ada5443de68cb8 100644 --- a/kernel/include/los_err.h +++ b/kernel/include/los_err.h @@ -143,6 +143,7 @@ enum LOS_MOUDLE_ID { LOS_MOD_MUX = 0X1d, LOS_MOD_CPUP = 0x1e, LOS_MOD_HOOK = 0x1f, + LOS_MOD_PERF = 0x20, LOS_MOD_SHELL = 0x31, LOS_MOD_DRIVER = 0x41, LOS_MOD_BUTT diff --git a/kernel/include/los_mp.h b/kernel/include/los_mp.h index dcb3e4cdc56b705f7740f864f57c757d2526d46b..67bbd39c7bb532de74d087d39fa6d52e1f42872d 100644 --- a/kernel/include/los_mp.h +++ b/kernel/include/los_mp.h @@ -33,6 +33,7 @@ #define _LOS_MP_H #include "los_config.h" +#include "los_list.h" #ifdef __cplusplus #if __cplusplus @@ -48,8 +49,13 @@ typedef enum { LOS_MP_IPI_WAKEUP, LOS_MP_IPI_SCHEDULE, LOS_MP_IPI_HALT, +#ifdef LOSCFG_KERNEL_SMP_CALL + LOS_MP_IPI_FUNC_CALL, +#endif } MP_IPI_TYPE; +typedef VOID (*SMP_FUNC_CALL)(VOID *args); + #ifdef LOSCFG_KERNEL_SMP extern VOID LOS_MpSchedule(UINT32 target); extern VOID OsMpWakeHandler(VOID); @@ -63,6 +69,28 @@ STATIC INLINE VOID LOS_MpSchedule(UINT32 target) } #endif +#ifdef LOSCFG_KERNEL_SMP_CALL +typedef struct { + LOS_DL_LIST node; + SMP_FUNC_CALL func; + VOID *args; +} MpCallFunc; + +/** + * It is used to call function on target cpus by sending ipi, and the first param is target cpu mask value. + */ +extern VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args); +extern VOID OsMpFuncCallHandler(VOID); +#else +INLINE VOID OsMpFuncCall(UINT32 target, SMP_FUNC_CALL func, VOID *args) +{ + (VOID)target; + if (func != NULL) { + func(args); + } +} +#endif /* LOSCFG_KERNEL_SMP_CALL */ + #ifdef __cplusplus #if __cplusplus } diff --git a/kernel/include/los_perf.h b/kernel/include/los_perf.h new file mode 100644 index 0000000000000000000000000000000000000000..a28dca767adc1a0e006a37354bca7e556bf58aa5 --- /dev/null +++ b/kernel/include/los_perf.h @@ -0,0 +1,439 @@ +/* + * 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. + */ + +/** + * @defgroup los_perf Perf + * @ingroup kernel + */ + +#ifndef _LOS_PERF_H +#define _LOS_PERF_H + +#include "los_typedef.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +/** + * @ingroup los_perf + * Perf max sample filter task number. + */ +#define PERF_MAX_FILTER_TSKS 32 + +/** + * @ingroup los_perf + * Perf max sample event counter's number. + */ +#define PERF_MAX_EVENT 7 + +/** + * @ingroup los_perf + * Perf max backtrace depth. + */ +#define PERF_MAX_CALLCHAIN_DEPTH 10 + +/** + * @ingroup los_perf + * Perf sample data buffer's water mark 1/N. + */ +#define PERF_BUFFER_WATERMARK_ONE_N 2 + +/** + * @ingroup los_perf + * Perf status. + */ +enum PerfStatus { + PERF_UNINIT, /* perf isn't inited */ + PERF_STARTED, /* perf is started */ + PERF_STOPPED, /* perf is stopped */ +}; + +/** + * @ingroup los_perf + * Define the type of the perf sample data buffer water mark hook function. + * + */ +typedef VOID (*PERF_BUF_NOTIFY_HOOK)(VOID); + +/** + * @ingroup los_perf + * Define the type of the perf sample data buffer flush hook function. + * + */ +typedef VOID (*PERF_BUF_FLUSH_HOOK)(VOID *addr, UINT32 size); + +/** + * @ingroup los_perf + * Perf error code: Bad status. + * + * Value: 0x02002000 + * + * Solution: Follow the perf state machine. + */ +#define LOS_ERRNO_PERF_STATUS_INVALID LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x00) + +/** + * @ingroup los_perf + * Perf error code: Hardware pmu init failed. + * + * Value: 0x02002001 + * + * Solution: Check the pmu hwi irq. + */ +#define LOS_ERRNO_PERF_HW_INIT_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x01) + +/** + * @ingroup los_perf + * Perf error code: Hrtimer init failed for hrtimer timed pmu init. + * + * Value: 0x02002002 + * + * Solution: Check the Hrtimer init. + */ +#define LOS_ERRNO_PERF_TIMED_INIT_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x02) + +/** + * @ingroup los_perf + * Perf error code: Software pmu init failed. + * + * Value: 0x02002003 + * + * Solution: Check the Perf software events init. + */ +#define LOS_ERRNO_PERF_SW_INIT_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x03) + +/** + * @ingroup los_perf + * Perf error code: Perf buffer init failed. + * + * Value: 0x02002004 + * + * Solution: Check the buffer init size. + */ +#define LOS_ERRNO_PERF_BUF_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x04) + +/** + * @ingroup los_perf + * Perf error code: Perf pmu type error. + * + * Value: 0x02002005 + * + * Solution: Check whether the corresponding pmu is enabled in the menuconfig. + */ +#define LOS_ERRNO_PERF_INVALID_PMU LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x05) + +/** + * @ingroup los_perf + * Perf error code: Perf pmu config error. + * + * Value: 0x02002006 + * + * Solution: Check the config attr of event id and event period. + */ +#define LOS_ERRNO_PERF_PMU_CONFIG_ERROR LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x06) + +/** + * @ingroup los_perf + * Perf error code: Perf pmu config attr is NULL. + * + * Value: 0x02002007 + * + * Solution: Check if the input params of attr is NULL. + */ +#define LOS_ERRNO_PERF_CONFIG_NULL LOS_ERRNO_OS_ERROR(LOS_MOD_PERF, 0x07) + +/** + * @ingroup los_perf + * Perf types + */ +enum PerfEventType { + PERF_EVENT_TYPE_HW, /* boards common hw events */ + PERF_EVENT_TYPE_TIMED, /* hrtimer timed events */ + PERF_EVENT_TYPE_SW, /* software trace events */ + PERF_EVENT_TYPE_RAW, /* boards special hw events, see enum PmuEventType in corresponding arch headfile */ + + PERF_EVENT_TYPE_MAX +}; + +/** + * @ingroup los_perf + * Common hardware pmu events + */ +enum PmuHwId { + PERF_COUNT_HW_CPU_CYCLES = 0, /* cpu cycle event */ + PERF_COUNT_HW_INSTRUCTIONS, /* instruction event */ + PERF_COUNT_HW_DCACHE_REFERENCES, /* dcache access event */ + PERF_COUNT_HW_DCACHE_MISSES, /* dcache miss event */ + PERF_COUNT_HW_ICACHE_REFERENCES, /* icache access event */ + PERF_COUNT_HW_ICACHE_MISSES, /* icache miss event */ + PERF_COUNT_HW_BRANCH_INSTRUCTIONS, /* software change of pc event */ + PERF_COUNT_HW_BRANCH_MISSES, /* branch miss event */ + + PERF_COUNT_HW_MAX, +}; + +/** + * @ingroup los_perf + * Common hrtimer timed events + */ +enum PmuTimedId { + PERF_COUNT_CPU_CLOCK = 0, /* hrtimer timed event */ +}; + +/** + * @ingroup los_perf + * Common software pmu events + */ +enum PmuSwId { + PERF_COUNT_SW_TASK_SWITCH = 1, /* task switch event */ + PERF_COUNT_SW_IRQ_RESPONSE, /* irq response event */ + PERF_COUNT_SW_MEM_ALLOC, /* memory alloc event */ + PERF_COUNT_SW_MUX_PEND, /* mutex pend event */ + + PERF_COUNT_SW_MAX, +}; + +/** + * @ingroup los_perf + * perf sample data types + * Config it through PerfConfigAttr->sampleType. + */ +enum PerfSampleType { + PERF_RECORD_CPU = 1U << 0, /* record current cpuid */ + PERF_RECORD_TID = 1U << 1, /* record current task id */ + PERF_RECORD_TYPE = 1U << 2, /* record event type */ + PERF_RECORD_PERIOD = 1U << 3, /* record event period */ + PERF_RECORD_TIMESTAMP = 1U << 4, /* record timestamp */ + PERF_RECORD_IP = 1U << 5, /* record instruction pointer */ + PERF_RECORD_CALLCHAIN = 1U << 6, /* record backtrace */ + PERF_RECORD_PID = 1U << 7, /* record current process id */ +}; + +/** + * @ingroup los_perf + * perf configuration sub event information + * + * This structure is used to config specific events attributes. + */ +typedef struct { + UINT32 type; /* enum PerfEventType */ + struct { + UINT32 eventId; /* the specific event corresponds to the PerfEventType */ + UINT32 period; /* event period, for every "period"th occurrence of the event a + sample will be recorded */ + } events[PERF_MAX_EVENT]; /* perf event list */ + UINT32 eventsNr; /* total perf event number */ + BOOL predivided; /* whether to prescaler (once every 64 counts), + which only take effect on cpu cycle hardware event */ +} PerfEventConfig; + +/** + * @ingroup los_perf + * perf configuration main information + * + * This structure is used to set perf sampling attributes, including events, tasks and other information. + */ +typedef struct { + PerfEventConfig eventsCfg; /* perf event config */ + UINT32 taskIds[PERF_MAX_FILTER_TSKS]; /* perf task filter list (allowlist) */ + UINT32 taskIdsNr; /* task numbers of task filter allowlist, + if set 0 perf will sample all tasks */ + UINT32 processIds[PERF_MAX_FILTER_TSKS]; /* perf process filter list (allowlist) */ + UINT32 processIdsNr; /* process numbers of process filter allowlist, + if set 0 perf will sample all processes */ + UINT32 sampleType; /* type of data to sample defined in PerfSampleType */ + BOOL needSample; /* whether to sample data */ +} PerfConfigAttr; + +/** + * @ingroup los_perf + * @brief Init perf. + * + * @par Description: + *
    + *
  • Used to initialize the perf module, including initializing the PMU, allocating memory, + * etc.,which is called during the phase of system initialization.
  • + *
+ * @attention + *
    + *
  • If buf is not NULL, user must ensure size is not bigger than buf's length.
  • + *
+ * + * @param buf [IN] Pointer of sample data buffer;Use the dynamically allocated memory if the pointer is NULL. + * @param size [IN] Length of sample data buffer. + * + * @retval #LOS_ERRNO_PERF_STATUS_INVALID Perf in a wrong status. + * @retval #LOS_ERRNO_PERF_HW_INIT_ERROR Perf hardware pmu init fail. + * @retval #LOS_ERRNO_PERF_TIMED_INIT_ERROR Perf timed pmu init fail. + * @retval #LOS_ERRNO_PERF_SW_INIT_ERROR Perf software pmu init fail. + * @retval #LOS_ERRNO_PERF_BUF_ERROR Perf buffer init fail. + * @retval #LOS_OK Perf init success. + * @par Dependency: + *
    + *
  • los_perf.h: the header file that contains the API declaration.
  • + *
+ */ +UINT32 LOS_PerfInit(VOID *buf, UINT32 size); + +/** + * @ingroup los_perf + * @brief Start perf sampling. + * + * @par Description + * Start perf sampling. + * @attention + * None. + * + * @param sectionId [IN] Set the section id for marking this piece of data in the perf sample data buffer. + * @retval None. + * @par Dependency: + *
    + *
  • los_perf.h: the header file that contains the API declaration.
  • + *
+ */ +VOID LOS_PerfStart(UINT32 sectionId); + +/** + * @ingroup los_perf + * @brief Stop perf sampling. + * + * @par Description + * Stop perf sampling. + * @attention + * None. + * + * @param None. + * + * @retval None. + * @par Dependency: + *
    + *
  • los_perf.h: the header file that contains the API declaration.
  • + *
+ */ +VOID LOS_PerfStop(VOID); + +/** + * @ingroup los_perf + * @brief Config perf parameters. + * + * @par Description + * Config perf parameters before sample, for example, sample event, sample task, etc. This interface need to be called + * before LOS_PerfStart. + * @attention + * None. + * + * @param attr [IN] Address of a perf event attr struct. + * + * @retval #LOS_ERRNO_PERF_STATUS_INVALID Perf in a wrong status. + * @retval #LOS_ERRNO_PERF_CONFIG_NULL Attr is NULL. + * @retval #LOS_ERRNO_PERF_INVALID_PMU Config perf pmu with error type. + * @retval #LOS_ERRNO_PERF_PMU_CONFIG_ERROR Config perf events fail with invalid event id or event period. + * @retval #LOS_OK Config success. + * @par Dependency: + *
    + *
  • los_perf.h: the header file that contains the API declaration.
  • + *
+ */ +UINT32 LOS_PerfConfig(PerfConfigAttr *attr); + +/** + * @ingroup los_perf + * @brief Read data from perf sample data buffer. + * + * @par Description + * Because perf sample data buffer is a ringbuffer, the data may be covered after user read ringbuffer. + * @attention + * None. + * + * @param dest [IN] The destination address. + * @param size [IN] Read size. + * @retval #UINT32 The really read bytes. + * @par Dependency: + *
    + *
  • los_perf.h: the header file that contains the API declaration.
  • + *
+ */ +UINT32 LOS_PerfDataRead(CHAR *dest, UINT32 size); + +/** + * @ingroup los_perf + * @brief Register perf sample data buffer water mark hook function. + * + * @par Description + *
    + *
  • Register perf sample data buffer water mark hook function.
  • + *
  • The registered hook will be called when buffer reaches the water mark./li> + *
+ * @attention + * None. + * + * @param func [IN] Buffer water mark hook function. + * + * @retval None. + * @par Dependency: + *
    + *
  • los_perf.h: the header file that contains the API declaration.
  • + *
+ */ +VOID LOS_PerfNotifyHookReg(const PERF_BUF_NOTIFY_HOOK func); + +/** + * @ingroup los_perf + * @brief Register perf sample data buffer flush hook function. + * + * @par Description + *
    + *
  • Register perf sample data buffer flush hook function.
  • + *
  • The flush hook will be called when the buffer be read or written.
  • + *
+ * @attention + * None. + * + * @param func [IN] Buffer flush hook function. + * + * @retval None. + * @par Dependency: + *
    + *
  • los_perf.h: the header file that contains the API declaration.
  • + *
+ */ +VOID LOS_PerfFlushHookReg(const PERF_BUF_FLUSH_HOOK func); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif /* _LOS_PERF_H */ diff --git a/tools/build/mk/los_config.mk b/tools/build/mk/los_config.mk index b9bad187b64adb810e720c5ae239b0ff1d71a10c..87d1e7214284538f8038bd8ee0ef5982b26e4d73 100644 --- a/tools/build/mk/los_config.mk +++ b/tools/build/mk/los_config.mk @@ -157,6 +157,12 @@ ifeq ($(LOSCFG_KERNEL_HOOK), y) LITEOS_HOOK_INCLUDE += -I $(LITEOSTOPDIR)/kernel/extended/hook/include endif +ifeq ($(LOSCFG_KERNEL_PERF), y) + LITEOS_BASELIB += -lperf + LIB_SUBDIRS += kernel/extended/perf + LITEOS_PERF_INCLUDE += -I $(LITEOSTOPDIR)/kernel/extended/perf +endif + ifeq ($(LOSCFG_KERNEL_LITEIPC), y) LITEOS_BASELIB += -lliteipc LIB_SUBDIRS += kernel/extended/liteipc @@ -366,6 +372,12 @@ ifeq ($(LOSCFG_DRIVERS_TRACE), y) LIB_SUBDIRS += $(LITEOSTOPDIR)/drivers/char/trace endif +ifeq ($(LOSCFG_DRIVERS_PERF), y) + LITEOS_BASELIB += -lperf_dev + LIB_SUBDIRS += $(LITEOSTOPDIR)/drivers/char/perf + LITEOS_DEV_PERF_INCLUDE += -I $(LITEOSTOPDIR)/drivers/char/perf/include +endif + ifeq ($(LOSCFG_DRIVERS_QUICKSTART), y) LITEOS_BASELIB += -lquickstart LIB_SUBDIRS += $(LITEOSTOPDIR)/drivers/char/quickstart @@ -494,7 +506,8 @@ endif LITEOS_EXTKERNEL_INCLUDE := $(LITEOS_CPPSUPPORT_INCLUDE) $(LITEOS_DYNLOAD_INCLUDE) \ $(LITEOS_TICKLESS_INCLUDE) $(LITEOS_HOOK_INCLUDE)\ $(LITEOS_VDSO_INCLUDE) $(LITEOS_LITEIPC_INCLUDE) \ - $(LITEOS_PIPE_INCLUDE) $(LITEOS_CPUP_INCLUDE) + $(LITEOS_PIPE_INCLUDE) $(LITEOS_CPUP_INCLUDE) \ + $(LITEOS_PERF_INCLUDE) LITEOS_COMPAT_INCLUDE := $(LITEOS_POSIX_INCLUDE) $(LITEOS_LINUX_INCLUDE) \ $(LITEOS_BSD_INCLUDE) LITEOS_FS_INCLUDE := $(LITEOS_VFS_INCLUDE) $(LITEOS_FAT_CACHE_INCLUDE) \ @@ -516,7 +529,7 @@ LITEOS_DRIVERS_INCLUDE := $(LITEOS_CELLWISE_INCLUDE) $(LITEOS_GPIO_INCLUDE $(LITEOS_REGULATOR_INCLUDE) $(LITEOS_VIDEO_INCLUDE) \ $(LITEOS_DRIVERS_HDF_INCLUDE) $(LITEOS_TZDRIVER_INCLUDE) \ $(LITEOS_HIEVENT_INCLUDE) $(LITEOS_DEV_MEM_INCLUDE) \ - $(LITEOS_DEV_QUICKSTART_INCLUDE) + $(LITEOS_DEV_QUICKSTART_INCLUDE) $(LITEOS_DEV_PERF_INCLUDE) LITEOS_DFX_INCLUDE := $(LITEOS_HILOG_INCLUDE) \ $(LITEOS_BLACKBOX_INCLUDE) \ $(LITEOS_HIDUMPER_INCLUDE)