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