]>
Commit | Line | Data |
---|---|---|
619c3a42 | 1 | /* |
0cdd5b13 | 2 | * Copyright (c) 2015, 2016, 2019 Nicira, Inc. |
619c3a42 AZ |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | /* This implementation only applies to the Linux platform. */ | |
9a6156bb AZ |
18 | |
19 | #include <config.h> | |
0cdd5b13 | 20 | #if defined(__linux__) && defined(HAVE_LINUX_PERF_EVENT_H) && !__CHECKER__ |
619c3a42 AZ |
21 | |
22 | #include <stddef.h> | |
23 | #include <sys/types.h> | |
24 | #include <stdlib.h> | |
25 | #include <unistd.h> | |
26 | #include <sys/ioctl.h> | |
27 | #include <linux/perf_event.h> | |
28 | #include <asm/unistd.h> | |
3e8a2ad1 | 29 | #include "openvswitch/dynamic-string.h" |
619c3a42 | 30 | #include "perf-counter.h" |
ee89ea7b | 31 | #include "openvswitch/shash.h" |
619c3a42 AZ |
32 | #include "util.h" |
33 | ||
cd747abb | 34 | static struct shash perf_counters = SHASH_INITIALIZER(&perf_counters); |
619c3a42 AZ |
35 | static int fd__ = 0; |
36 | ||
37 | uint64_t | |
38 | perf_counter_read(uint64_t *counter) | |
39 | { | |
8c0294bd | 40 | int size = sizeof *counter; |
296527df AZ |
41 | |
42 | if (fd__ <= 0 || read(fd__, counter, size) < size) { | |
619c3a42 AZ |
43 | *counter = 0; |
44 | } | |
45 | ||
46 | return *counter; | |
47 | } | |
48 | ||
49 | static long | |
50 | perf_event_open(struct perf_event_attr *hw_event, pid_t pid, | |
51 | int cpu, int group_fd, unsigned long flags) | |
52 | { | |
53 | int ret; | |
54 | ||
55 | ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, | |
56 | group_fd, flags); | |
57 | return ret; | |
58 | } | |
59 | ||
60 | /* Set up perf event counters to read user space instruction counters | |
61 | * only for this process, on all cpus. */ | |
62 | static void | |
63 | perf_event_setup(void) | |
64 | { | |
65 | struct perf_event_attr pe; | |
66 | ||
67 | memset(&pe, 0, sizeof(struct perf_event_attr)); | |
68 | pe.type = PERF_TYPE_HARDWARE; | |
69 | pe.size = sizeof(struct perf_event_attr); | |
70 | pe.config = PERF_COUNT_HW_INSTRUCTIONS; | |
71 | pe.disabled = 1; | |
72 | pe.exclude_kernel = 1; | |
73 | pe.exclude_hv = 1; | |
74 | ||
75 | fd__ = perf_event_open(&pe, 0, -1, -1, 0); | |
8790a8b4 | 76 | if (fd__ > 0) { |
619c3a42 AZ |
77 | ioctl(fd__, PERF_EVENT_IOC_RESET, 0); |
78 | ioctl(fd__, PERF_EVENT_IOC_ENABLE, 0); | |
79 | } | |
80 | } | |
81 | ||
82 | static void | |
83 | perf_counter_init(struct perf_counter *counter) | |
84 | { | |
85 | counter->once = true; | |
86 | shash_add_assert(&perf_counters, counter->name, counter); | |
87 | } | |
88 | ||
89 | void | |
90 | perf_counter_accumulate(struct perf_counter *counter, uint64_t start_count) | |
91 | { | |
92 | uint64_t end_count; | |
93 | ||
94 | if (!counter->once) { | |
95 | perf_counter_init(counter); | |
96 | } | |
97 | ||
98 | counter->n_events++; | |
99 | perf_counter_read(&end_count); | |
100 | counter->total_count += end_count - start_count; | |
101 | } | |
102 | ||
103 | static void | |
104 | perf_counter_to_ds(struct ds *ds, struct perf_counter *pfc) | |
105 | { | |
106 | double ratio; | |
107 | ||
108 | if (pfc->n_events) { | |
109 | ratio = (double)pfc->total_count / (double)pfc->n_events; | |
110 | } else { | |
111 | ratio = 0.0; | |
112 | } | |
113 | ||
cd9e88ee | 114 | ds_put_format(ds, "%-40s %12"PRIu64" %12"PRIu64" %12.1f\n", |
f084224a | 115 | pfc->name, pfc->n_events, pfc->total_count, ratio); |
619c3a42 AZ |
116 | } |
117 | ||
118 | static void | |
119 | perf_counters_to_ds(struct ds *ds) | |
120 | { | |
121 | const char *err_str; | |
122 | const struct shash_node **sorted; | |
123 | int i; | |
124 | ||
125 | err_str = NULL; | |
126 | if (fd__ == -1) { | |
127 | err_str = "performance counter is not supported on this platfrom"; | |
128 | } else if (!shash_count(&perf_counters)) { | |
129 | err_str = "performance counter has never been hit"; | |
130 | } | |
131 | ||
132 | if (err_str) { | |
133 | ds_put_format(ds, "%s\n", err_str); | |
134 | return; | |
135 | } | |
136 | ||
137 | /* Display counters in alphabetical order. */ | |
138 | sorted = shash_sort(&perf_counters); | |
139 | for (i = 0; i < shash_count(&perf_counters); i++) { | |
140 | perf_counter_to_ds(ds, sorted[i]->data); | |
141 | } | |
142 | free(sorted); | |
143 | } | |
144 | ||
145 | /* | |
146 | * Caller is responsible for free memory. | |
147 | */ | |
148 | char * | |
37f4615e | 149 | perf_counters_to_string(void) |
619c3a42 AZ |
150 | { |
151 | struct ds ds; | |
152 | ||
153 | ds_init(&ds); | |
154 | perf_counters_to_ds(&ds); | |
155 | return ds_steal_cstr(&ds); | |
156 | } | |
157 | ||
158 | void | |
159 | perf_counters_init(void) | |
160 | { | |
161 | shash_init(&perf_counters); | |
162 | perf_event_setup(); | |
163 | } | |
164 | ||
165 | void | |
166 | perf_counters_clear(void) | |
167 | { | |
168 | struct shash_node *node; | |
169 | ||
170 | SHASH_FOR_EACH (node, &perf_counters) { | |
171 | struct perf_counter *perf = node->data; | |
172 | ||
173 | perf->n_events = 0; | |
174 | perf->total_count = 0; | |
175 | } | |
176 | } | |
177 | ||
178 | void | |
37f4615e | 179 | perf_counters_destroy(void) |
619c3a42 AZ |
180 | { |
181 | struct shash_node *node, *next; | |
182 | ||
183 | if (fd__ != -1) { | |
184 | ioctl(fd__, PERF_EVENT_IOC_DISABLE, 0); | |
185 | close(fd__); | |
186 | } | |
187 | ||
188 | SHASH_FOR_EACH_SAFE (node, next, &perf_counters) { | |
189 | shash_delete(&perf_counters, node); | |
190 | } | |
191 | ||
192 | shash_destroy(&perf_counters); | |
193 | } | |
194 | #endif |