1 /* 2 * Copyright(C) 2015 Linaro Limited. All rights reserved. 3 * Author: Mathieu Poirier <mathieu.poirier@linaro.org> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 as published by 7 * the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <stdbool.h> 19 #include <linux/coresight-pmu.h> 20 21 #include "../../util/auxtrace.h" 22 #include "../../util/evlist.h" 23 #include "../../util/pmu.h" 24 #include "cs-etm.h" 25 #include "arm-spe.h" 26 27 static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) 28 { 29 struct perf_pmu **arm_spe_pmus = NULL; 30 int ret, i, nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 31 /* arm_spe_xxxxxxxxx\0 */ 32 char arm_spe_pmu_name[sizeof(ARM_SPE_PMU_NAME) + 10]; 33 34 arm_spe_pmus = zalloc(sizeof(struct perf_pmu *) * nr_cpus); 35 if (!arm_spe_pmus) { 36 pr_err("spes alloc failed\n"); 37 *err = -ENOMEM; 38 return NULL; 39 } 40 41 for (i = 0; i < nr_cpus; i++) { 42 ret = sprintf(arm_spe_pmu_name, "%s%d", ARM_SPE_PMU_NAME, i); 43 if (ret < 0) { 44 pr_err("sprintf failed\n"); 45 *err = -ENOMEM; 46 return NULL; 47 } 48 49 arm_spe_pmus[*nr_spes] = perf_pmu__find(arm_spe_pmu_name); 50 if (arm_spe_pmus[*nr_spes]) { 51 pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n", 52 __func__, __LINE__, *nr_spes, 53 arm_spe_pmus[*nr_spes]->type, 54 arm_spe_pmus[*nr_spes]->name); 55 (*nr_spes)++; 56 } 57 } 58 59 return arm_spe_pmus; 60 } 61 62 struct auxtrace_record 63 *auxtrace_record__init(struct perf_evlist *evlist, int *err) 64 { 65 struct perf_pmu *cs_etm_pmu; 66 struct perf_evsel *evsel; 67 bool found_etm = false; 68 bool found_spe = false; 69 static struct perf_pmu **arm_spe_pmus = NULL; 70 static int nr_spes = 0; 71 int i = 0; 72 73 if (!evlist) 74 return NULL; 75 76 cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); 77 78 if (!arm_spe_pmus) 79 arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err); 80 81 evlist__for_each_entry(evlist, evsel) { 82 if (cs_etm_pmu && 83 evsel->attr.type == cs_etm_pmu->type) 84 found_etm = true; 85 86 if (!nr_spes) 87 continue; 88 89 for (i = 0; i < nr_spes; i++) { 90 if (evsel->attr.type == arm_spe_pmus[i]->type) { 91 found_spe = true; 92 break; 93 } 94 } 95 } 96 97 if (found_etm && found_spe) { 98 pr_err("Concurrent ARM Coresight ETM and SPE operation not currently supported\n"); 99 *err = -EOPNOTSUPP; 100 return NULL; 101 } 102 103 if (found_etm) 104 return cs_etm_record_init(err); 105 106 #if defined(__aarch64__) 107 if (found_spe) 108 return arm_spe_recording_init(err, arm_spe_pmus[i]); 109 #endif 110 111 /* 112 * Clear 'err' even if we haven't found an event - that way perf 113 * record can still be used even if tracers aren't present. The NULL 114 * return value will take care of telling the infrastructure HW tracing 115 * isn't available. 116 */ 117 *err = 0; 118 return NULL; 119 } 120