diff options
author | Ingo Molnar <mingo@kernel.org> | 2014-06-13 02:19:06 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-06-13 02:19:06 -0400 |
commit | 4ba96195051be30160af6d5f5f83f9a055ab1f23 (patch) | |
tree | 7af7f287ce8c723393fa014288da900eb03a3641 | |
parent | 7184062b94b4bfac08715fb786fd2df399c5d6ee (diff) | |
parent | 45dc1bb5c1d47f9519e2101f6b073bb4bb1d1f99 (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf into perf/core
Pull perf/core improvements and fixes from Jiri Olsa:
* Honor user freq/interval properly in record command (Namhyung Kim)
* Speedup DWARF unwind (Jiri Olsa)
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/perf/tests/builtin-test.c | 42 | ||||
-rw-r--r-- | tools/perf/tests/dso-data.c | 214 | ||||
-rw-r--r-- | tools/perf/tests/tests.h | 2 | ||||
-rw-r--r-- | tools/perf/util/dso.c | 279 | ||||
-rw-r--r-- | tools/perf/util/dso.h | 50 | ||||
-rw-r--r-- | tools/perf/util/event.h | 5 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 4 | ||||
-rw-r--r-- | tools/perf/util/perf_regs.c | 10 | ||||
-rw-r--r-- | tools/perf/util/perf_regs.h | 4 | ||||
-rw-r--r-- | tools/perf/util/unwind-libunwind.c | 2 |
10 files changed, 574 insertions, 38 deletions
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 802e3cd50f6f..6f8b01bc6033 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -3,6 +3,8 @@ | |||
3 | * | 3 | * |
4 | * Builtin regression testing command: ever growing number of sanity tests | 4 | * Builtin regression testing command: ever growing number of sanity tests |
5 | */ | 5 | */ |
6 | #include <unistd.h> | ||
7 | #include <string.h> | ||
6 | #include "builtin.h" | 8 | #include "builtin.h" |
7 | #include "intlist.h" | 9 | #include "intlist.h" |
8 | #include "tests.h" | 10 | #include "tests.h" |
@@ -50,10 +52,18 @@ static struct test { | |||
50 | .func = test__pmu, | 52 | .func = test__pmu, |
51 | }, | 53 | }, |
52 | { | 54 | { |
53 | .desc = "Test dso data interface", | 55 | .desc = "Test dso data read", |
54 | .func = test__dso_data, | 56 | .func = test__dso_data, |
55 | }, | 57 | }, |
56 | { | 58 | { |
59 | .desc = "Test dso data cache", | ||
60 | .func = test__dso_data_cache, | ||
61 | }, | ||
62 | { | ||
63 | .desc = "Test dso data reopen", | ||
64 | .func = test__dso_data_reopen, | ||
65 | }, | ||
66 | { | ||
57 | .desc = "roundtrip evsel->name check", | 67 | .desc = "roundtrip evsel->name check", |
58 | .func = test__perf_evsel__roundtrip_name_test, | 68 | .func = test__perf_evsel__roundtrip_name_test, |
59 | }, | 69 | }, |
@@ -172,6 +182,34 @@ static bool perf_test__matches(int curr, int argc, const char *argv[]) | |||
172 | return false; | 182 | return false; |
173 | } | 183 | } |
174 | 184 | ||
185 | static int run_test(struct test *test) | ||
186 | { | ||
187 | int status, err = -1, child = fork(); | ||
188 | |||
189 | if (child < 0) { | ||
190 | pr_err("failed to fork test: %s\n", strerror(errno)); | ||
191 | return -1; | ||
192 | } | ||
193 | |||
194 | if (!child) { | ||
195 | pr_debug("test child forked, pid %d\n", getpid()); | ||
196 | err = test->func(); | ||
197 | exit(err); | ||
198 | } | ||
199 | |||
200 | wait(&status); | ||
201 | |||
202 | if (WIFEXITED(status)) { | ||
203 | err = WEXITSTATUS(status); | ||
204 | pr_debug("test child finished with %d\n", err); | ||
205 | } else if (WIFSIGNALED(status)) { | ||
206 | err = -1; | ||
207 | pr_debug("test child interrupted\n"); | ||
208 | } | ||
209 | |||
210 | return err; | ||
211 | } | ||
212 | |||
175 | static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) | 213 | static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) |
176 | { | 214 | { |
177 | int i = 0; | 215 | int i = 0; |
@@ -200,7 +238,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) | |||
200 | } | 238 | } |
201 | 239 | ||
202 | pr_debug("\n--- start ---\n"); | 240 | pr_debug("\n--- start ---\n"); |
203 | err = tests[curr].func(); | 241 | err = run_test(&tests[curr]); |
204 | pr_debug("---- end ----\n%s:", tests[curr].desc); | 242 | pr_debug("---- end ----\n%s:", tests[curr].desc); |
205 | 243 | ||
206 | switch (err) { | 244 | switch (err) { |
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index 3e6cb171e3d3..630808cd7cc2 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c | |||
@@ -1,22 +1,27 @@ | |||
1 | #include "util.h" | ||
2 | |||
3 | #include <stdlib.h> | 1 | #include <stdlib.h> |
4 | #include <linux/types.h> | 2 | #include <linux/types.h> |
5 | #include <sys/stat.h> | 3 | #include <sys/stat.h> |
6 | #include <fcntl.h> | 4 | #include <fcntl.h> |
7 | #include <string.h> | 5 | #include <string.h> |
8 | 6 | #include <sys/time.h> | |
7 | #include <sys/resource.h> | ||
8 | #include <api/fs/fs.h> | ||
9 | #include "util.h" | ||
9 | #include "machine.h" | 10 | #include "machine.h" |
10 | #include "symbol.h" | 11 | #include "symbol.h" |
11 | #include "tests.h" | 12 | #include "tests.h" |
12 | 13 | ||
13 | static char *test_file(int size) | 14 | static char *test_file(int size) |
14 | { | 15 | { |
15 | static char buf_templ[] = "/tmp/test-XXXXXX"; | 16 | #define TEMPL "/tmp/perf-test-XXXXXX" |
17 | static char buf_templ[sizeof(TEMPL)]; | ||
16 | char *templ = buf_templ; | 18 | char *templ = buf_templ; |
17 | int fd, i; | 19 | int fd, i; |
18 | unsigned char *buf; | 20 | unsigned char *buf; |
19 | 21 | ||
22 | strcpy(buf_templ, TEMPL); | ||
23 | #undef TEMPL | ||
24 | |||
20 | fd = mkstemp(templ); | 25 | fd = mkstemp(templ); |
21 | if (fd < 0) { | 26 | if (fd < 0) { |
22 | perror("mkstemp failed"); | 27 | perror("mkstemp failed"); |
@@ -150,3 +155,204 @@ int test__dso_data(void) | |||
150 | unlink(file); | 155 | unlink(file); |
151 | return 0; | 156 | return 0; |
152 | } | 157 | } |
158 | |||
159 | static long open_files_cnt(void) | ||
160 | { | ||
161 | char path[PATH_MAX]; | ||
162 | struct dirent *dent; | ||
163 | DIR *dir; | ||
164 | long nr = 0; | ||
165 | |||
166 | scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint()); | ||
167 | pr_debug("fd path: %s\n", path); | ||
168 | |||
169 | dir = opendir(path); | ||
170 | TEST_ASSERT_VAL("failed to open fd directory", dir); | ||
171 | |||
172 | while ((dent = readdir(dir)) != NULL) { | ||
173 | if (!strcmp(dent->d_name, ".") || | ||
174 | !strcmp(dent->d_name, "..")) | ||
175 | continue; | ||
176 | |||
177 | nr++; | ||
178 | } | ||
179 | |||
180 | closedir(dir); | ||
181 | return nr - 1; | ||
182 | } | ||
183 | |||
184 | static struct dso **dsos; | ||
185 | |||
186 | static int dsos__create(int cnt, int size) | ||
187 | { | ||
188 | int i; | ||
189 | |||
190 | dsos = malloc(sizeof(dsos) * cnt); | ||
191 | TEST_ASSERT_VAL("failed to alloc dsos array", dsos); | ||
192 | |||
193 | for (i = 0; i < cnt; i++) { | ||
194 | char *file; | ||
195 | |||
196 | file = test_file(size); | ||
197 | TEST_ASSERT_VAL("failed to get dso file", file); | ||
198 | |||
199 | dsos[i] = dso__new(file); | ||
200 | TEST_ASSERT_VAL("failed to get dso", dsos[i]); | ||
201 | } | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static void dsos__delete(int cnt) | ||
207 | { | ||
208 | int i; | ||
209 | |||
210 | for (i = 0; i < cnt; i++) { | ||
211 | struct dso *dso = dsos[i]; | ||
212 | |||
213 | unlink(dso->name); | ||
214 | dso__delete(dso); | ||
215 | } | ||
216 | |||
217 | free(dsos); | ||
218 | } | ||
219 | |||
220 | static int set_fd_limit(int n) | ||
221 | { | ||
222 | struct rlimit rlim; | ||
223 | |||
224 | if (getrlimit(RLIMIT_NOFILE, &rlim)) | ||
225 | return -1; | ||
226 | |||
227 | pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n); | ||
228 | |||
229 | rlim.rlim_cur = n; | ||
230 | return setrlimit(RLIMIT_NOFILE, &rlim); | ||
231 | } | ||
232 | |||
233 | int test__dso_data_cache(void) | ||
234 | { | ||
235 | struct machine machine; | ||
236 | long nr_end, nr = open_files_cnt(); | ||
237 | int dso_cnt, limit, i, fd; | ||
238 | |||
239 | memset(&machine, 0, sizeof(machine)); | ||
240 | |||
241 | /* set as system limit */ | ||
242 | limit = nr * 4; | ||
243 | TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit)); | ||
244 | |||
245 | /* and this is now our dso open FDs limit + 1 extra */ | ||
246 | dso_cnt = limit / 2 + 1; | ||
247 | TEST_ASSERT_VAL("failed to create dsos\n", | ||
248 | !dsos__create(dso_cnt, TEST_FILE_SIZE)); | ||
249 | |||
250 | for (i = 0; i < (dso_cnt - 1); i++) { | ||
251 | struct dso *dso = dsos[i]; | ||
252 | |||
253 | /* | ||
254 | * Open dsos via dso__data_fd or dso__data_read_offset. | ||
255 | * Both opens the data file and keep it open. | ||
256 | */ | ||
257 | if (i % 2) { | ||
258 | fd = dso__data_fd(dso, &machine); | ||
259 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
260 | } else { | ||
261 | #define BUFSIZE 10 | ||
262 | u8 buf[BUFSIZE]; | ||
263 | ssize_t n; | ||
264 | |||
265 | n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE); | ||
266 | TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | /* open +1 dso over the allowed limit */ | ||
271 | fd = dso__data_fd(dsos[i], &machine); | ||
272 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
273 | |||
274 | /* should force the first one to be closed */ | ||
275 | TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1); | ||
276 | |||
277 | /* cleanup everything */ | ||
278 | dsos__delete(dso_cnt); | ||
279 | |||
280 | /* Make sure we did not leak any file descriptor. */ | ||
281 | nr_end = open_files_cnt(); | ||
282 | pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); | ||
283 | TEST_ASSERT_VAL("failed leadking files", nr == nr_end); | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | int test__dso_data_reopen(void) | ||
288 | { | ||
289 | struct machine machine; | ||
290 | long nr_end, nr = open_files_cnt(); | ||
291 | int fd, fd_extra; | ||
292 | |||
293 | #define dso_0 (dsos[0]) | ||
294 | #define dso_1 (dsos[1]) | ||
295 | #define dso_2 (dsos[2]) | ||
296 | |||
297 | memset(&machine, 0, sizeof(machine)); | ||
298 | |||
299 | /* | ||
300 | * Test scenario: | ||
301 | * - create 3 dso objects | ||
302 | * - set process file descriptor limit to current | ||
303 | * files count + 3 | ||
304 | * - test that the first dso gets closed when we | ||
305 | * reach the files count limit | ||
306 | */ | ||
307 | |||
308 | /* Make sure we are able to open 3 fds anyway */ | ||
309 | TEST_ASSERT_VAL("failed to set file limit", | ||
310 | !set_fd_limit((nr + 3))); | ||
311 | |||
312 | TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE)); | ||
313 | |||
314 | /* open dso_0 */ | ||
315 | fd = dso__data_fd(dso_0, &machine); | ||
316 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
317 | |||
318 | /* open dso_1 */ | ||
319 | fd = dso__data_fd(dso_1, &machine); | ||
320 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
321 | |||
322 | /* | ||
323 | * open extra file descriptor and we just | ||
324 | * reached the files count limit | ||
325 | */ | ||
326 | fd_extra = open("/dev/null", O_RDONLY); | ||
327 | TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0); | ||
328 | |||
329 | /* open dso_2 */ | ||
330 | fd = dso__data_fd(dso_2, &machine); | ||
331 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
332 | |||
333 | /* | ||
334 | * dso_0 should get closed, because we reached | ||
335 | * the file descriptor limit | ||
336 | */ | ||
337 | TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1); | ||
338 | |||
339 | /* open dso_0 */ | ||
340 | fd = dso__data_fd(dso_0, &machine); | ||
341 | TEST_ASSERT_VAL("failed to get fd", fd > 0); | ||
342 | |||
343 | /* | ||
344 | * dso_1 should get closed, because we reached | ||
345 | * the file descriptor limit | ||
346 | */ | ||
347 | TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1); | ||
348 | |||
349 | /* cleanup everything */ | ||
350 | close(fd_extra); | ||
351 | dsos__delete(3); | ||
352 | |||
353 | /* Make sure we did not leak any file descriptor. */ | ||
354 | nr_end = open_files_cnt(); | ||
355 | pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end); | ||
356 | TEST_ASSERT_VAL("failed leadking files", nr == nr_end); | ||
357 | return 0; | ||
358 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 022bb68fd9c7..ed64790a395f 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -28,6 +28,8 @@ int test__syscall_open_tp_fields(void); | |||
28 | int test__pmu(void); | 28 | int test__pmu(void); |
29 | int test__attr(void); | 29 | int test__attr(void); |
30 | int test__dso_data(void); | 30 | int test__dso_data(void); |
31 | int test__dso_data_cache(void); | ||
32 | int test__dso_data_reopen(void); | ||
31 | int test__parse_events(void); | 33 | int test__parse_events(void); |
32 | int test__hists_link(void); | 34 | int test__hists_link(void); |
33 | int test__python_use(void); | 35 | int test__python_use(void); |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 64453d63b971..819f10414f08 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -1,3 +1,6 @@ | |||
1 | #include <asm/bug.h> | ||
2 | #include <sys/time.h> | ||
3 | #include <sys/resource.h> | ||
1 | #include "symbol.h" | 4 | #include "symbol.h" |
2 | #include "dso.h" | 5 | #include "dso.h" |
3 | #include "machine.h" | 6 | #include "machine.h" |
@@ -136,7 +139,48 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
136 | return ret; | 139 | return ret; |
137 | } | 140 | } |
138 | 141 | ||
139 | static int open_dso(struct dso *dso, struct machine *machine) | 142 | /* |
143 | * Global list of open DSOs and the counter. | ||
144 | */ | ||
145 | static LIST_HEAD(dso__data_open); | ||
146 | static long dso__data_open_cnt; | ||
147 | |||
148 | static void dso__list_add(struct dso *dso) | ||
149 | { | ||
150 | list_add_tail(&dso->data.open_entry, &dso__data_open); | ||
151 | dso__data_open_cnt++; | ||
152 | } | ||
153 | |||
154 | static void dso__list_del(struct dso *dso) | ||
155 | { | ||
156 | list_del(&dso->data.open_entry); | ||
157 | WARN_ONCE(dso__data_open_cnt <= 0, | ||
158 | "DSO data fd counter out of bounds."); | ||
159 | dso__data_open_cnt--; | ||
160 | } | ||
161 | |||
162 | static void close_first_dso(void); | ||
163 | |||
164 | static int do_open(char *name) | ||
165 | { | ||
166 | int fd; | ||
167 | |||
168 | do { | ||
169 | fd = open(name, O_RDONLY); | ||
170 | if (fd >= 0) | ||
171 | return fd; | ||
172 | |||
173 | pr_debug("dso open failed, mmap: %s\n", strerror(errno)); | ||
174 | if (!dso__data_open_cnt || errno != EMFILE) | ||
175 | break; | ||
176 | |||
177 | close_first_dso(); | ||
178 | } while (1); | ||
179 | |||
180 | return -1; | ||
181 | } | ||
182 | |||
183 | static int __open_dso(struct dso *dso, struct machine *machine) | ||
140 | { | 184 | { |
141 | int fd; | 185 | int fd; |
142 | char *root_dir = (char *)""; | 186 | char *root_dir = (char *)""; |
@@ -154,11 +198,130 @@ static int open_dso(struct dso *dso, struct machine *machine) | |||
154 | return -EINVAL; | 198 | return -EINVAL; |
155 | } | 199 | } |
156 | 200 | ||
157 | fd = open(name, O_RDONLY); | 201 | fd = do_open(name); |
158 | free(name); | 202 | free(name); |
159 | return fd; | 203 | return fd; |
160 | } | 204 | } |
161 | 205 | ||
206 | static void check_data_close(void); | ||
207 | |||
208 | /** | ||
209 | * dso_close - Open DSO data file | ||
210 | * @dso: dso object | ||
211 | * | ||
212 | * Open @dso's data file descriptor and updates | ||
213 | * list/count of open DSO objects. | ||
214 | */ | ||
215 | static int open_dso(struct dso *dso, struct machine *machine) | ||
216 | { | ||
217 | int fd = __open_dso(dso, machine); | ||
218 | |||
219 | if (fd > 0) { | ||
220 | dso__list_add(dso); | ||
221 | /* | ||
222 | * Check if we crossed the allowed number | ||
223 | * of opened DSOs and close one if needed. | ||
224 | */ | ||
225 | check_data_close(); | ||
226 | } | ||
227 | |||
228 | return fd; | ||
229 | } | ||
230 | |||
231 | static void close_data_fd(struct dso *dso) | ||
232 | { | ||
233 | if (dso->data.fd >= 0) { | ||
234 | close(dso->data.fd); | ||
235 | dso->data.fd = -1; | ||
236 | dso->data.file_size = 0; | ||
237 | dso__list_del(dso); | ||
238 | } | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * dso_close - Close DSO data file | ||
243 | * @dso: dso object | ||
244 | * | ||
245 | * Close @dso's data file descriptor and updates | ||
246 | * list/count of open DSO objects. | ||
247 | */ | ||
248 | static void close_dso(struct dso *dso) | ||
249 | { | ||
250 | close_data_fd(dso); | ||
251 | } | ||
252 | |||
253 | static void close_first_dso(void) | ||
254 | { | ||
255 | struct dso *dso; | ||
256 | |||
257 | dso = list_first_entry(&dso__data_open, struct dso, data.open_entry); | ||
258 | close_dso(dso); | ||
259 | } | ||
260 | |||
261 | static rlim_t get_fd_limit(void) | ||
262 | { | ||
263 | struct rlimit l; | ||
264 | rlim_t limit = 0; | ||
265 | |||
266 | /* Allow half of the current open fd limit. */ | ||
267 | if (getrlimit(RLIMIT_NOFILE, &l) == 0) { | ||
268 | if (l.rlim_cur == RLIM_INFINITY) | ||
269 | limit = l.rlim_cur; | ||
270 | else | ||
271 | limit = l.rlim_cur / 2; | ||
272 | } else { | ||
273 | pr_err("failed to get fd limit\n"); | ||
274 | limit = 1; | ||
275 | } | ||
276 | |||
277 | return limit; | ||
278 | } | ||
279 | |||
280 | static bool may_cache_fd(void) | ||
281 | { | ||
282 | static rlim_t limit; | ||
283 | |||
284 | if (!limit) | ||
285 | limit = get_fd_limit(); | ||
286 | |||
287 | if (limit == RLIM_INFINITY) | ||
288 | return true; | ||
289 | |||
290 | return limit > (rlim_t) dso__data_open_cnt; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * Check and close LRU dso if we crossed allowed limit | ||
295 | * for opened dso file descriptors. The limit is half | ||
296 | * of the RLIMIT_NOFILE files opened. | ||
297 | */ | ||
298 | static void check_data_close(void) | ||
299 | { | ||
300 | bool cache_fd = may_cache_fd(); | ||
301 | |||
302 | if (!cache_fd) | ||
303 | close_first_dso(); | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * dso__data_close - Close DSO data file | ||
308 | * @dso: dso object | ||
309 | * | ||
310 | * External interface to close @dso's data file descriptor. | ||
311 | */ | ||
312 | void dso__data_close(struct dso *dso) | ||
313 | { | ||
314 | close_dso(dso); | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * dso__data_fd - Get dso's data file descriptor | ||
319 | * @dso: dso object | ||
320 | * @machine: machine object | ||
321 | * | ||
322 | * External interface to find dso's file, open it and | ||
323 | * returns file descriptor. | ||
324 | */ | ||
162 | int dso__data_fd(struct dso *dso, struct machine *machine) | 325 | int dso__data_fd(struct dso *dso, struct machine *machine) |
163 | { | 326 | { |
164 | enum dso_binary_type binary_type_data[] = { | 327 | enum dso_binary_type binary_type_data[] = { |
@@ -168,8 +331,13 @@ int dso__data_fd(struct dso *dso, struct machine *machine) | |||
168 | }; | 331 | }; |
169 | int i = 0; | 332 | int i = 0; |
170 | 333 | ||
171 | if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) | 334 | if (dso->data.fd >= 0) |
172 | return open_dso(dso, machine); | 335 | return dso->data.fd; |
336 | |||
337 | if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) { | ||
338 | dso->data.fd = open_dso(dso, machine); | ||
339 | return dso->data.fd; | ||
340 | } | ||
173 | 341 | ||
174 | do { | 342 | do { |
175 | int fd; | 343 | int fd; |
@@ -178,7 +346,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine) | |||
178 | 346 | ||
179 | fd = open_dso(dso, machine); | 347 | fd = open_dso(dso, machine); |
180 | if (fd >= 0) | 348 | if (fd >= 0) |
181 | return fd; | 349 | return dso->data.fd = fd; |
182 | 350 | ||
183 | } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); | 351 | } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); |
184 | 352 | ||
@@ -260,16 +428,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset, | |||
260 | } | 428 | } |
261 | 429 | ||
262 | static ssize_t | 430 | static ssize_t |
263 | dso_cache__read(struct dso *dso, struct machine *machine, | 431 | dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) |
264 | u64 offset, u8 *data, ssize_t size) | ||
265 | { | 432 | { |
266 | struct dso_cache *cache; | 433 | struct dso_cache *cache; |
267 | ssize_t ret; | 434 | ssize_t ret; |
268 | int fd; | ||
269 | |||
270 | fd = dso__data_fd(dso, machine); | ||
271 | if (fd < 0) | ||
272 | return -1; | ||
273 | 435 | ||
274 | do { | 436 | do { |
275 | u64 cache_offset; | 437 | u64 cache_offset; |
@@ -283,16 +445,16 @@ dso_cache__read(struct dso *dso, struct machine *machine, | |||
283 | cache_offset = offset & DSO__DATA_CACHE_MASK; | 445 | cache_offset = offset & DSO__DATA_CACHE_MASK; |
284 | ret = -EINVAL; | 446 | ret = -EINVAL; |
285 | 447 | ||
286 | if (-1 == lseek(fd, cache_offset, SEEK_SET)) | 448 | if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET)) |
287 | break; | 449 | break; |
288 | 450 | ||
289 | ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); | 451 | ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE); |
290 | if (ret <= 0) | 452 | if (ret <= 0) |
291 | break; | 453 | break; |
292 | 454 | ||
293 | cache->offset = cache_offset; | 455 | cache->offset = cache_offset; |
294 | cache->size = ret; | 456 | cache->size = ret; |
295 | dso_cache__insert(&dso->cache, cache); | 457 | dso_cache__insert(&dso->data.cache, cache); |
296 | 458 | ||
297 | ret = dso_cache__memcpy(cache, offset, data, size); | 459 | ret = dso_cache__memcpy(cache, offset, data, size); |
298 | 460 | ||
@@ -301,24 +463,27 @@ dso_cache__read(struct dso *dso, struct machine *machine, | |||
301 | if (ret <= 0) | 463 | if (ret <= 0) |
302 | free(cache); | 464 | free(cache); |
303 | 465 | ||
304 | close(fd); | ||
305 | return ret; | 466 | return ret; |
306 | } | 467 | } |
307 | 468 | ||
308 | static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, | 469 | static ssize_t dso_cache_read(struct dso *dso, u64 offset, |
309 | u64 offset, u8 *data, ssize_t size) | 470 | u8 *data, ssize_t size) |
310 | { | 471 | { |
311 | struct dso_cache *cache; | 472 | struct dso_cache *cache; |
312 | 473 | ||
313 | cache = dso_cache__find(&dso->cache, offset); | 474 | cache = dso_cache__find(&dso->data.cache, offset); |
314 | if (cache) | 475 | if (cache) |
315 | return dso_cache__memcpy(cache, offset, data, size); | 476 | return dso_cache__memcpy(cache, offset, data, size); |
316 | else | 477 | else |
317 | return dso_cache__read(dso, machine, offset, data, size); | 478 | return dso_cache__read(dso, offset, data, size); |
318 | } | 479 | } |
319 | 480 | ||
320 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | 481 | /* |
321 | u64 offset, u8 *data, ssize_t size) | 482 | * Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks |
483 | * in the rb_tree. Any read to already cached data is served | ||
484 | * by cached data. | ||
485 | */ | ||
486 | static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) | ||
322 | { | 487 | { |
323 | ssize_t r = 0; | 488 | ssize_t r = 0; |
324 | u8 *p = data; | 489 | u8 *p = data; |
@@ -326,7 +491,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | |||
326 | do { | 491 | do { |
327 | ssize_t ret; | 492 | ssize_t ret; |
328 | 493 | ||
329 | ret = dso_cache_read(dso, machine, offset, p, size); | 494 | ret = dso_cache_read(dso, offset, p, size); |
330 | if (ret < 0) | 495 | if (ret < 0) |
331 | return ret; | 496 | return ret; |
332 | 497 | ||
@@ -346,6 +511,67 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | |||
346 | return r; | 511 | return r; |
347 | } | 512 | } |
348 | 513 | ||
514 | static int data_file_size(struct dso *dso) | ||
515 | { | ||
516 | struct stat st; | ||
517 | |||
518 | if (!dso->data.file_size) { | ||
519 | if (fstat(dso->data.fd, &st)) { | ||
520 | pr_err("dso mmap failed, fstat: %s\n", strerror(errno)); | ||
521 | return -1; | ||
522 | } | ||
523 | dso->data.file_size = st.st_size; | ||
524 | } | ||
525 | |||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | static ssize_t data_read_offset(struct dso *dso, u64 offset, | ||
530 | u8 *data, ssize_t size) | ||
531 | { | ||
532 | if (data_file_size(dso)) | ||
533 | return -1; | ||
534 | |||
535 | /* Check the offset sanity. */ | ||
536 | if (offset > dso->data.file_size) | ||
537 | return -1; | ||
538 | |||
539 | if (offset + size < offset) | ||
540 | return -1; | ||
541 | |||
542 | return cached_read(dso, offset, data, size); | ||
543 | } | ||
544 | |||
545 | /** | ||
546 | * dso__data_read_offset - Read data from dso file offset | ||
547 | * @dso: dso object | ||
548 | * @machine: machine object | ||
549 | * @offset: file offset | ||
550 | * @data: buffer to store data | ||
551 | * @size: size of the @data buffer | ||
552 | * | ||
553 | * External interface to read data from dso file offset. Open | ||
554 | * dso data file and use cached_read to get the data. | ||
555 | */ | ||
556 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
557 | u64 offset, u8 *data, ssize_t size) | ||
558 | { | ||
559 | if (dso__data_fd(dso, machine) < 0) | ||
560 | return -1; | ||
561 | |||
562 | return data_read_offset(dso, offset, data, size); | ||
563 | } | ||
564 | |||
565 | /** | ||
566 | * dso__data_read_addr - Read data from dso address | ||
567 | * @dso: dso object | ||
568 | * @machine: machine object | ||
569 | * @add: virtual memory address | ||
570 | * @data: buffer to store data | ||
571 | * @size: size of the @data buffer | ||
572 | * | ||
573 | * External interface to read data from dso address. | ||
574 | */ | ||
349 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | 575 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, |
350 | struct machine *machine, u64 addr, | 576 | struct machine *machine, u64 addr, |
351 | u8 *data, ssize_t size) | 577 | u8 *data, ssize_t size) |
@@ -473,7 +699,8 @@ struct dso *dso__new(const char *name) | |||
473 | dso__set_short_name(dso, dso->name, false); | 699 | dso__set_short_name(dso, dso->name, false); |
474 | for (i = 0; i < MAP__NR_TYPES; ++i) | 700 | for (i = 0; i < MAP__NR_TYPES; ++i) |
475 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; | 701 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; |
476 | dso->cache = RB_ROOT; | 702 | dso->data.cache = RB_ROOT; |
703 | dso->data.fd = -1; | ||
477 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; | 704 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; |
478 | dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; | 705 | dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; |
479 | dso->loaded = 0; | 706 | dso->loaded = 0; |
@@ -485,6 +712,7 @@ struct dso *dso__new(const char *name) | |||
485 | dso->kernel = DSO_TYPE_USER; | 712 | dso->kernel = DSO_TYPE_USER; |
486 | dso->needs_swap = DSO_SWAP__UNSET; | 713 | dso->needs_swap = DSO_SWAP__UNSET; |
487 | INIT_LIST_HEAD(&dso->node); | 714 | INIT_LIST_HEAD(&dso->node); |
715 | INIT_LIST_HEAD(&dso->data.open_entry); | ||
488 | } | 716 | } |
489 | 717 | ||
490 | return dso; | 718 | return dso; |
@@ -506,7 +734,8 @@ void dso__delete(struct dso *dso) | |||
506 | dso->long_name_allocated = false; | 734 | dso->long_name_allocated = false; |
507 | } | 735 | } |
508 | 736 | ||
509 | dso_cache__free(&dso->cache); | 737 | dso__data_close(dso); |
738 | dso_cache__free(&dso->data.cache); | ||
510 | dso__free_a2l(dso); | 739 | dso__free_a2l(dso); |
511 | zfree(&dso->symsrc_filename); | 740 | zfree(&dso->symsrc_filename); |
512 | free(dso); | 741 | free(dso); |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 38efe95a7fdd..ad553ba257bf 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -76,7 +76,6 @@ struct dso { | |||
76 | struct list_head node; | 76 | struct list_head node; |
77 | struct rb_root symbols[MAP__NR_TYPES]; | 77 | struct rb_root symbols[MAP__NR_TYPES]; |
78 | struct rb_root symbol_names[MAP__NR_TYPES]; | 78 | struct rb_root symbol_names[MAP__NR_TYPES]; |
79 | struct rb_root cache; | ||
80 | void *a2l; | 79 | void *a2l; |
81 | char *symsrc_filename; | 80 | char *symsrc_filename; |
82 | unsigned int a2l_fails; | 81 | unsigned int a2l_fails; |
@@ -99,6 +98,15 @@ struct dso { | |||
99 | const char *long_name; | 98 | const char *long_name; |
100 | u16 long_name_len; | 99 | u16 long_name_len; |
101 | u16 short_name_len; | 100 | u16 short_name_len; |
101 | |||
102 | /* dso data file */ | ||
103 | struct { | ||
104 | struct rb_root cache; | ||
105 | int fd; | ||
106 | size_t file_size; | ||
107 | struct list_head open_entry; | ||
108 | } data; | ||
109 | |||
102 | char name[0]; | 110 | char name[0]; |
103 | }; | 111 | }; |
104 | 112 | ||
@@ -141,7 +149,47 @@ char dso__symtab_origin(const struct dso *dso); | |||
141 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, | 149 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, |
142 | char *root_dir, char *filename, size_t size); | 150 | char *root_dir, char *filename, size_t size); |
143 | 151 | ||
152 | /* | ||
153 | * The dso__data_* external interface provides following functions: | ||
154 | * dso__data_fd | ||
155 | * dso__data_close | ||
156 | * dso__data_read_offset | ||
157 | * dso__data_read_addr | ||
158 | * | ||
159 | * Please refer to the dso.c object code for each function and | ||
160 | * arguments documentation. Following text tries to explain the | ||
161 | * dso file descriptor caching. | ||
162 | * | ||
163 | * The dso__data* interface allows caching of opened file descriptors | ||
164 | * to speed up the dso data accesses. The idea is to leave the file | ||
165 | * descriptor opened ideally for the whole life of the dso object. | ||
166 | * | ||
167 | * The current usage of the dso__data_* interface is as follows: | ||
168 | * | ||
169 | * Get DSO's fd: | ||
170 | * int fd = dso__data_fd(dso, machine); | ||
171 | * USE 'fd' SOMEHOW | ||
172 | * | ||
173 | * Read DSO's data: | ||
174 | * n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE); | ||
175 | * n = dso__data_read_addr(dso_0, &machine, 0, buf, BUFSIZE); | ||
176 | * | ||
177 | * Eventually close DSO's fd: | ||
178 | * dso__data_close(dso); | ||
179 | * | ||
180 | * It is not necessary to close the DSO object data file. Each time new | ||
181 | * DSO data file is opened, the limit (RLIMIT_NOFILE/2) is checked. Once | ||
182 | * it is crossed, the oldest opened DSO object is closed. | ||
183 | * | ||
184 | * The dso__delete function calls close_dso function to ensure the | ||
185 | * data file descriptor gets closed/unmapped before the dso object | ||
186 | * is freed. | ||
187 | * | ||
188 | * TODO | ||
189 | */ | ||
144 | int dso__data_fd(struct dso *dso, struct machine *machine); | 190 | int dso__data_fd(struct dso *dso, struct machine *machine); |
191 | void dso__data_close(struct dso *dso); | ||
192 | |||
145 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | 193 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, |
146 | u64 offset, u8 *data, ssize_t size); | 194 | u64 offset, u8 *data, ssize_t size); |
147 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | 195 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 9ba2eb3bdcfd..e5dd40addb30 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "../perf.h" | 7 | #include "../perf.h" |
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "build-id.h" | 9 | #include "build-id.h" |
10 | #include "perf_regs.h" | ||
10 | 11 | ||
11 | struct mmap_event { | 12 | struct mmap_event { |
12 | struct perf_event_header header; | 13 | struct perf_event_header header; |
@@ -89,6 +90,10 @@ struct regs_dump { | |||
89 | u64 abi; | 90 | u64 abi; |
90 | u64 mask; | 91 | u64 mask; |
91 | u64 *regs; | 92 | u64 *regs; |
93 | |||
94 | /* Cached values/mask filled by first register access. */ | ||
95 | u64 cache_regs[PERF_REGS_MAX]; | ||
96 | u64 cache_mask; | ||
92 | }; | 97 | }; |
93 | 98 | ||
94 | struct stack_dump { | 99 | struct stack_dump { |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 21154dabc5fa..8606175fe1e8 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -589,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
589 | } | 589 | } |
590 | 590 | ||
591 | /* | 591 | /* |
592 | * We default some events to a 1 default interval. But keep | 592 | * We default some events to have a default interval. But keep |
593 | * it a weak assumption overridable by the user. | 593 | * it a weak assumption overridable by the user. |
594 | */ | 594 | */ |
595 | if (!attr->sample_period || (opts->user_freq != UINT_MAX && | 595 | if (!attr->sample_period || (opts->user_freq != UINT_MAX || |
596 | opts->user_interval != ULLONG_MAX)) { | 596 | opts->user_interval != ULLONG_MAX)) { |
597 | if (opts->freq) { | 597 | if (opts->freq) { |
598 | perf_evsel__set_sample_bit(evsel, PERIOD); | 598 | perf_evsel__set_sample_bit(evsel, PERIOD); |
diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index a3539ef30b15..43168fb0d9a2 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c | |||
@@ -1,11 +1,15 @@ | |||
1 | #include <errno.h> | 1 | #include <errno.h> |
2 | #include "perf_regs.h" | 2 | #include "perf_regs.h" |
3 | #include "event.h" | ||
3 | 4 | ||
4 | int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) | 5 | int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) |
5 | { | 6 | { |
6 | int i, idx = 0; | 7 | int i, idx = 0; |
7 | u64 mask = regs->mask; | 8 | u64 mask = regs->mask; |
8 | 9 | ||
10 | if (regs->cache_mask & (1 << id)) | ||
11 | goto out; | ||
12 | |||
9 | if (!(mask & (1 << id))) | 13 | if (!(mask & (1 << id))) |
10 | return -EINVAL; | 14 | return -EINVAL; |
11 | 15 | ||
@@ -14,6 +18,10 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) | |||
14 | idx++; | 18 | idx++; |
15 | } | 19 | } |
16 | 20 | ||
17 | *valp = regs->regs[idx]; | 21 | regs->cache_mask |= (1 << id); |
22 | regs->cache_regs[id] = regs->regs[idx]; | ||
23 | |||
24 | out: | ||
25 | *valp = regs->cache_regs[id]; | ||
18 | return 0; | 26 | return 0; |
19 | } | 27 | } |
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 79c78f74e0cf..980dbf76bc98 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h | |||
@@ -2,7 +2,8 @@ | |||
2 | #define __PERF_REGS_H | 2 | #define __PERF_REGS_H |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include "event.h" | 5 | |
6 | struct regs_dump; | ||
6 | 7 | ||
7 | #ifdef HAVE_PERF_REGS_SUPPORT | 8 | #ifdef HAVE_PERF_REGS_SUPPORT |
8 | #include <perf_regs.h> | 9 | #include <perf_regs.h> |
@@ -11,6 +12,7 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id); | |||
11 | 12 | ||
12 | #else | 13 | #else |
13 | #define PERF_REGS_MASK 0 | 14 | #define PERF_REGS_MASK 0 |
15 | #define PERF_REGS_MAX 0 | ||
14 | 16 | ||
15 | static inline const char *perf_reg_name(int id __maybe_unused) | 17 | static inline const char *perf_reg_name(int id __maybe_unused) |
16 | { | 18 | { |
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index bd5768d74f01..25578b98f5c5 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c | |||
@@ -250,7 +250,6 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, | |||
250 | 250 | ||
251 | /* Check the .eh_frame section for unwinding info */ | 251 | /* Check the .eh_frame section for unwinding info */ |
252 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | 252 | offset = elf_section_offset(fd, ".eh_frame_hdr"); |
253 | close(fd); | ||
254 | 253 | ||
255 | if (offset) | 254 | if (offset) |
256 | ret = unwind_spec_ehframe(dso, machine, offset, | 255 | ret = unwind_spec_ehframe(dso, machine, offset, |
@@ -271,7 +270,6 @@ static int read_unwind_spec_debug_frame(struct dso *dso, | |||
271 | 270 | ||
272 | /* Check the .debug_frame section for unwinding info */ | 271 | /* Check the .debug_frame section for unwinding info */ |
273 | *offset = elf_section_offset(fd, ".debug_frame"); | 272 | *offset = elf_section_offset(fd, ".debug_frame"); |
274 | close(fd); | ||
275 | 273 | ||
276 | if (*offset) | 274 | if (*offset) |
277 | return 0; | 275 | return 0; |