Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2015 Nicira, Inc.
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. */
18 : :
19 : : #include <config.h>
20 : : #if defined(__linux__) && defined(HAVE_LINUX_PERF_EVENT_H)
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>
29 : : #include "openvswitch/dynamic-string.h"
30 : : #include "perf-counter.h"
31 : : #include "openvswitch/shash.h"
32 : : #include "util.h"
33 : :
34 : : static struct shash perf_counters = SHASH_INITIALIZER(&perf_counters);
35 : : static int fd__ = 0;
36 : :
37 : : uint64_t
38 : 31932 : perf_counter_read(uint64_t *counter)
39 : : {
40 : 31932 : int size = sizeof *counter;
41 : :
42 [ - + ][ # # ]: 31932 : if (fd__ <= 0 || read(fd__, counter, size) < size) {
43 : 31932 : *counter = 0;
44 : : }
45 : :
46 : 31932 : return *counter;
47 : : }
48 : :
49 : : static long
50 : 1260 : 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 : 1260 : ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
56 : : group_fd, flags);
57 : 1260 : 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 : 1260 : perf_event_setup(void)
64 : : {
65 : : struct perf_event_attr pe;
66 : :
67 : 1260 : memset(&pe, 0, sizeof(struct perf_event_attr));
68 : 1260 : pe.type = PERF_TYPE_HARDWARE;
69 : 1260 : pe.size = sizeof(struct perf_event_attr);
70 : 1260 : pe.config = PERF_COUNT_HW_INSTRUCTIONS;
71 : 1260 : pe.disabled = 1;
72 : 1260 : pe.exclude_kernel = 1;
73 : 1260 : pe.exclude_hv = 1;
74 : :
75 : 1260 : fd__ = perf_event_open(&pe, 0, -1, -1, 0);
76 [ - + ]: 1260 : if (fd__ > 0) {
77 : 0 : ioctl(fd__, PERF_EVENT_IOC_RESET, 0);
78 : 0 : ioctl(fd__, PERF_EVENT_IOC_ENABLE, 0);
79 : : }
80 : 1260 : }
81 : :
82 : : static void
83 : 1542 : perf_counter_init(struct perf_counter *counter)
84 : : {
85 : 1542 : counter->once = true;
86 : 1542 : shash_add_assert(&perf_counters, counter->name, counter);
87 : 1542 : }
88 : :
89 : : void
90 : 15966 : perf_counter_accumulate(struct perf_counter *counter, uint64_t start_count)
91 : : {
92 : : uint64_t end_count;
93 : :
94 [ + + ]: 15966 : if (!counter->once) {
95 : 1542 : perf_counter_init(counter);
96 : : }
97 : :
98 : 15966 : counter->n_events++;
99 : 15966 : perf_counter_read(&end_count);
100 : 15966 : counter->total_count += end_count - start_count;
101 : 15966 : }
102 : :
103 : : static void
104 : 0 : perf_counter_to_ds(struct ds *ds, struct perf_counter *pfc)
105 : : {
106 : : double ratio;
107 : :
108 [ # # ]: 0 : if (pfc->n_events) {
109 : 0 : ratio = (double)pfc->total_count / (double)pfc->n_events;
110 : : } else {
111 : 0 : ratio = 0.0;
112 : : }
113 : :
114 : 0 : ds_put_format(ds, "%-40s%12"PRIu64"%12"PRIu64"%12.1f\n",
115 : : pfc->name, pfc->n_events, pfc->total_count, ratio);
116 : 0 : }
117 : :
118 : : static void
119 : 0 : perf_counters_to_ds(struct ds *ds)
120 : : {
121 : : const char *err_str;
122 : : const struct shash_node **sorted;
123 : : int i;
124 : :
125 : 0 : err_str = NULL;
126 [ # # ]: 0 : if (fd__ == -1) {
127 : 0 : err_str = "performance counter is not supported on this platfrom";
128 [ # # ]: 0 : } else if (!shash_count(&perf_counters)) {
129 : 0 : err_str = "performance counter has never been hit";
130 : : }
131 : :
132 [ # # ]: 0 : if (err_str) {
133 : 0 : ds_put_format(ds, "%s\n", err_str);
134 : 0 : return;
135 : : }
136 : :
137 : : /* Display counters in alphabetical order. */
138 : 0 : sorted = shash_sort(&perf_counters);
139 [ # # ]: 0 : for (i = 0; i < shash_count(&perf_counters); i++) {
140 : 0 : perf_counter_to_ds(ds, sorted[i]->data);
141 : : }
142 : 0 : free(sorted);
143 : : }
144 : :
145 : : /*
146 : : * Caller is responsible for free memory.
147 : : */
148 : : char *
149 : 0 : perf_counters_to_string()
150 : : {
151 : : struct ds ds;
152 : :
153 : 0 : ds_init(&ds);
154 : 0 : perf_counters_to_ds(&ds);
155 : 0 : return ds_steal_cstr(&ds);
156 : : }
157 : :
158 : : void
159 : 1260 : perf_counters_init(void)
160 : : {
161 : 1260 : shash_init(&perf_counters);
162 : 1260 : perf_event_setup();
163 : 1260 : }
164 : :
165 : : void
166 : 0 : perf_counters_clear(void)
167 : : {
168 : : struct shash_node *node;
169 : :
170 [ # # ][ # # ]: 0 : SHASH_FOR_EACH (node, &perf_counters) {
171 : 0 : struct perf_counter *perf = node->data;
172 : :
173 : 0 : perf->n_events = 0;
174 : 0 : perf->total_count = 0;
175 : : }
176 : 0 : }
177 : :
178 : : void
179 : 1258 : perf_counters_destroy()
180 : : {
181 : : struct shash_node *node, *next;
182 : :
183 [ - + ]: 1258 : if (fd__ != -1) {
184 : 0 : ioctl(fd__, PERF_EVENT_IOC_DISABLE, 0);
185 : 0 : close(fd__);
186 : : }
187 : :
188 [ + + ][ - + ]: 2516 : SHASH_FOR_EACH_SAFE (node, next, &perf_counters) {
[ + + ]
189 : 1258 : shash_delete(&perf_counters, node);
190 : : }
191 : :
192 : 1258 : shash_destroy(&perf_counters);
193 : 1258 : }
194 : : #endif
|