aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2014-06-13 02:19:06 -0400
committerIngo Molnar <mingo@kernel.org>2014-06-13 02:19:06 -0400
commit4ba96195051be30160af6d5f5f83f9a055ab1f23 (patch)
tree7af7f287ce8c723393fa014288da900eb03a3641 /tools/perf/util
parent7184062b94b4bfac08715fb786fd2df399c5d6ee (diff)
parent45dc1bb5c1d47f9519e2101f6b073bb4bb1d1f99 (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>
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/dso.c279
-rw-r--r--tools/perf/util/dso.h50
-rw-r--r--tools/perf/util/event.h5
-rw-r--r--tools/perf/util/evsel.c4
-rw-r--r--tools/perf/util/perf_regs.c10
-rw-r--r--tools/perf/util/perf_regs.h4
-rw-r--r--tools/perf/util/unwind-libunwind.c2
7 files changed, 322 insertions, 32 deletions
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
139static int open_dso(struct dso *dso, struct machine *machine) 142/*
143 * Global list of open DSOs and the counter.
144 */
145static LIST_HEAD(dso__data_open);
146static long dso__data_open_cnt;
147
148static 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
154static 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
162static void close_first_dso(void);
163
164static 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
183static 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
206static 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 */
215static 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
231static 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 */
248static void close_dso(struct dso *dso)
249{
250 close_data_fd(dso);
251}
252
253static 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
261static 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
280static 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*/
298static 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 */
312void 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 */
162int dso__data_fd(struct dso *dso, struct machine *machine) 325int 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
262static ssize_t 430static ssize_t
263dso_cache__read(struct dso *dso, struct machine *machine, 431dso_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
308static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, 469static 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
320ssize_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 */
486static 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
514static 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
529static 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 */
556ssize_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 */
349ssize_t dso__data_read_addr(struct dso *dso, struct map *map, 575ssize_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);
141int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, 149int 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*/
144int dso__data_fd(struct dso *dso, struct machine *machine); 190int dso__data_fd(struct dso *dso, struct machine *machine);
191void dso__data_close(struct dso *dso);
192
145ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, 193ssize_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);
147ssize_t dso__data_read_addr(struct dso *dso, struct map *map, 195ssize_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
11struct mmap_event { 12struct 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
94struct stack_dump { 99struct 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
4int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) 5int 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
24out:
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
6struct 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
15static inline const char *perf_reg_name(int id __maybe_unused) 17static 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;