1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "perf_counter.h"
17
18 #ifndef unlikely
19 #define unlikely(x) __builtin_expect(!!(x), 0)
20 #endif
21 #ifndef likely
22 #define likely(x) __builtin_expect(!!(x), 1)
23 #endif
24
25 #ifdef PERF_MONITOR
26
27 static const char* config_file = "perf_config.txt";
28 static const char* output_file = "perf_result.txt";
29 static pthread_mutex_t __pw_mutex = PTHREAD_MUTEX_INITIALIZER;
30 static int __perf_write = 0;
31 static std::atomic<int> __perf_init = 0;
32
33 static pthread_mutex_t __g_stat_mutex = PTHREAD_MUTEX_INITIALIZER;
34 static std::vector<struct PerfStat*>g_perfstat;
35
36 static int n_event = 5;
37 static int even_type = PERF_TYPE_SOFTWARE;
38 static int pmu_event[MAX_COUNTERS] = {PERF_COUNT_SW_CPU_CLOCK, PERF_COUNT_SW_TASK_CLOCK, PERF_COUNT_SW_PAGE_FAULTS,
39 PERF_COUNT_SW_CONTEXT_SWITCHES, PERF_COUNT_SW_CPU_MIGRATIONS};
40
41 static __thread struct PerfStat* t_perfStat = NULL;
42
__gettid(void)43 static inline pid_t __gettid(void)
44 {
45 return syscall(__NR_gettid);
46 }
47
perf_event_open(struct perf_event_attr * attr,pid_t pid,int cpu,int group_fd,unsigned long flags)48 static inline int perf_event_open(struct perf_event_attr* attr, pid_t pid, int cpu, int group_fd, unsigned long flags)
49 {
50 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
51 }
52
perf_open(struct PerfStat * pf,int event)53 static void perf_open(struct PerfStat* pf, int event)
54 {
55 struct perf_event_attr attr = {0};
56
57 attr.size = sizeof(struct perf_event_attr);
58 attr.type = even_type;
59 attr.config = event;
60 attr.disabled = 1;
61 attr.exclude_kernel = 1;
62 attr.exclude_hv = 1;
63 attr.read_format = PERF_FORMAT_GROUP;
64
65 // calling process/thread on any CPU
66 /********************************************************************************************************
67 detail in https://man7.org/linux/man-pages/man2/perf_event_open.2.html
68
69 int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags);
70 pid == 0 and cpu == -1
71 This measures the calling process/thread on any CPU.
72
73 pid == 0 and cpu >= 0
74 This measures the calling process/thread only when running
75 on the specified CPU.
76
77 pid > 0 and cpu == -1
78 This measures the specified process/thread on any CPU.
79
80 pid > 0 and cpu >= 0
81 This measures the specified process/thread only when
82 running on the specified CPU.
83
84 pid == -1 and cpu >= 0
85 This measures all processes/threads on the specified CPU.
86 This requires CAP_PERFMON (since Linux 5.8) or
87 CAP_SYS_ADMIN capability or a
88 /proc/sys/kernel/perf_event_paranoid value of less than 1.
89
90 pid == -1 and cpu == -1
91 This setting is invalid and will return an error.
92
93 *********************************************************************************************************/
94 int ret = perf_event_open(&attr, pf->pid, -1, pf->perfFD, 0);
95 if (ret < 0) {
96 return;
97 }
98
99 if (pf->perfFD == -1) {
100 pf->perfFD = ret;
101 }
102 pf->nCounters++;
103 }
104
perf_init(void)105 static void perf_init(void)
106 {
107 std::fstream file(config_file, std::ios::in);
108 if (!file) {
109 printf("perf_config.txt not exist.\n");
110 return;
111 }
112
113 if (!(file >> even_type)) {
114 printf("perf event type not exist.\n");
115 file.close();
116 return;
117 }
118
119 if (!(file >> n_event)) {
120 printf("perf event num not exist.\n");
121 file.close();
122 return;
123 }
124
125 if ((n_event > MAX_COUNTERS) || (even_type > PERF_TYPE_MAX)) {
126 printf("pmu config err type:%d, num:%d.\n", even_type, n_event);
127 file.close();
128 return;
129 }
130
131 for (int i = 0; i < n_event; i++) {
132 if (!(file >> pmu_event[i]))
133 break;
134 printf("pmu event id:%d.\n", pmu_event[i]);
135 }
136
137 file.close();
138 }
139
140 struct perf_ignore {
141 const char* ig_task;
142 int ignore_count;
143 };
144
145 static struct perf_ignore __g_ignore[] = {
146 {"cpu_work", 2},
147 {"task_build", 2},
148 };
149
find_ignore(struct PerfRecord * record)150 static struct perf_ignore* find_ignore(struct PerfRecord* record)
151 {
152 int i;
153 int size = sizeof(__g_ignore) / sizeof(struct perf_ignore);
154 for (i = 0; i < size; i++) {
155 if (strncmp(__g_ignore[i].ig_task, record->name, NAME_LEN) == 0) {
156 return &__g_ignore[i];
157 }
158 }
159
160 return nullptr;
161 }
162
perf_ignore(struct PerfRecord * record)163 static bool perf_ignore(struct PerfRecord* record)
164 {
165 struct perf_ignore* p = find_ignore(record);
166 if ((!p) || (!p->ignore_count)) {
167 return false;
168 }
169 p->ignore_count--;
170 return true;
171 }
172
perf_counter_output(struct PerfStat * stat)173 static void perf_counter_output(struct PerfStat* stat)
174 {
175 if (!stat) {
176 printf("no perf stat,tid:%d\n", __gettid());
177 return;
178 }
179 std::map<std::string, Counters> m_counters;
180
181 pthread_mutex_lock(&__pw_mutex);
182
183 FILE* fd = fopen(output_file, __perf_write == 0 ? (__perf_write = 1, "wt") : "a");
184 if (!fd) {
185 printf("perf_result.txt creat err.\n");
186 pthread_mutex_unlock(&__pw_mutex);
187 return;
188 }
189
190 auto doRecord = [&](struct PerfRecord* record) {
191 auto it = m_counters.find(record->name);
192 if (it != m_counters.end()) {
193 it->second.nr++;
194 for (int k = 0; k < MAX_COUNTERS; k++) {
195 it->second.vals[k] += (record->countersEnd.vals[k] - record->countersBegin.vals[k]);
196 }
197 } else {
198 Counters new_;
199 new_.nr = 1;
200 for (int k = 0; k < MAX_COUNTERS; k++) {
201 new_.vals[k] = (record->countersEnd.vals[k] - record->countersBegin.vals[k]);
202 }
203 m_counters.insert(std::pair<std::string, Counters>(record->name, new_));
204 }
205 };
206
207 for (int i = 0; i < TASK_NUM; i++) {
208 struct PerfTask* task = &stat->perfTask[i];
209 int max_rd = (task->rd > RECORD_NUM) ? RECORD_NUM : task->rd;
210 for (int j = 0; j < max_rd; j++) {
211 struct PerfRecord* record = &task->perfRecord[j];
212 if (!(record->beginFlag) || !(record->endFlag)) {
213 continue;
214 }
215 if (perf_ignore(record)) {
216 continue;
217 }
218
219 doRecord(record);
220 }
221 }
222
223 for (auto iter = m_counters.begin(); iter != m_counters.end(); iter++) {
224 fprintf_s(fd,
225 "pid:%d, taskname:%s, taskid:%d, recordid:%d, evt_num:%d, pmu_%x:%lu, pmu_%x:%lu, pmu_%x:%lu, pmu_%x:%lu, "
226 "pmu_%x:%lu, pmu_%x:%lu, pmu_%x:%lu, nr:%lu.\n",
227 stat->pid, iter->first.c_str(), 0, 1, stat->nCounters, pmu_event[0], iter->second.vals[0], pmu_event[1],
228 iter->second.vals[1], pmu_event[2], iter->second.vals[2], pmu_event[3], iter->second.vals[3], pmu_event[4],
229 iter->second.vals[4], pmu_event[5], iter->second.vals[5], pmu_event[6], iter->second.vals[6],
230 iter->second.nr);
231 }
232
233 fclose(fd);
234 pthread_mutex_unlock(&__pw_mutex);
235 m_counters.clear();
236 }
237 #endif
238