License cleanup: add SPDX GPL-2.0 license identifier to files with no license
Many source files in the tree are missing licensing information, which
makes it harder for compliance tools to determine the correct license.
By default all files without license information are under the default
license of the kernel, which is GPL version 2.
Update the files which contain no license information with the 'GPL-2.0'
SPDX license identifier. The SPDX identifier is a legally binding
shorthand, which can be used instead of the full boiler plate text.
This patch is based on work done by Thomas Gleixner and Kate Stewart and
Philippe Ombredanne.
How this work was done:
Patches were generated and checked against linux-4.14-rc6 for a subset of
the use cases:
- file had no licensing information it it.
- file was a */uapi/* one with no licensing information in it,
- file was a */uapi/* one with existing licensing information,
Further patches will be generated in subsequent months to fix up cases
where non-standard license headers were used, and references to license
had to be inferred by heuristics based on keywords.
The analysis to determine which SPDX License Identifier to be applied to
a file was done in a spreadsheet of side by side results from of the
output of two independent scanners (ScanCode & Windriver) producing SPDX
tag:value files created by Philippe Ombredanne. Philippe prepared the
base worksheet, and did an initial spot review of a few 1000 files.
The 4.13 kernel was the starting point of the analysis with 60,537 files
assessed. Kate Stewart did a file by file comparison of the scanner
results in the spreadsheet to determine which SPDX license identifier(s)
to be applied to the file. She confirmed any determination that was not
immediately clear with lawyers working with the Linux Foundation.
Criteria used to select files for SPDX license identifier tagging was:
- Files considered eligible had to be source code files.
- Make and config files were included as candidates if they contained >5
lines of source
- File already had some variant of a license header in it (even if <5
lines).
All documentation files were explicitly excluded.
The following heuristics were used to determine which SPDX license
identifiers to apply.
- when both scanners couldn't find any license traces, file was
considered to have no license information in it, and the top level
COPYING file license applied.
For non */uapi/* files that summary was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 11139
and resulted in the first patch in this series.
If that file was a */uapi/* path one, it was "GPL-2.0 WITH
Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 WITH Linux-syscall-note 930
and resulted in the second patch in this series.
- if a file had some form of licensing information in it, and was one
of the */uapi/* ones, it was denoted with the Linux-syscall-note if
any GPL family license was found in the file or had no licensing in
it (per prior point). Results summary:
SPDX license identifier # files
---------------------------------------------------|------
GPL-2.0 WITH Linux-syscall-note 270
GPL-2.0+ WITH Linux-syscall-note 169
((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21
((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17
LGPL-2.1+ WITH Linux-syscall-note 15
GPL-1.0+ WITH Linux-syscall-note 14
((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5
LGPL-2.0+ WITH Linux-syscall-note 4
LGPL-2.1 WITH Linux-syscall-note 3
((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3
((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1
and that resulted in the third patch in this series.
- when the two scanners agreed on the detected license(s), that became
the concluded license(s).
- when there was disagreement between the two scanners (one detected a
license but the other didn't, or they both detected different
licenses) a manual inspection of the file occurred.
- In most cases a manual inspection of the information in the file
resulted in a clear resolution of the license that should apply (and
which scanner probably needed to revisit its heuristics).
- When it was not immediately clear, the license identifier was
confirmed with lawyers working with the Linux Foundation.
- If there was any question as to the appropriate license identifier,
the file was flagged for further research and to be revisited later
in time.
In total, over 70 hours of logged manual review was done on the
spreadsheet to determine the SPDX license identifiers to apply to the
source files by Kate, Philippe, Thomas and, in some cases, confirmation
by lawyers working with the Linux Foundation.
Kate also obtained a third independent scan of the 4.13 code base from
FOSSology, and compared selected files where the other two scanners
disagreed against that SPDX file, to see if there was new insights. The
Windriver scanner is based on an older version of FOSSology in part, so
they are related.
Thomas did random spot checks in about 500 files from the spreadsheets
for the uapi headers and agreed with SPDX license identifier in the
files he inspected. For the non-uapi files Thomas did random spot checks
in about 15000 files.
In initial set of patches against 4.14-rc6, 3 files were found to have
copy/paste license identifier errors, and have been fixed to reflect the
correct identifier.
Additionally Philippe spent 10 hours this week doing a detailed manual
inspection and review of the 12,461 patched files from the initial patch
version early this week with:
- a full scancode scan run, collecting the matched texts, detected
license ids and scores
- reviewing anything where there was a license detected (about 500+
files) to ensure that the applied SPDX license was correct
- reviewing anything where there was no detection but the patch license
was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied
SPDX license was correct
This produced a worksheet with 20 files needing minor correction. This
worksheet was then exported into 3 different .csv files for the
different types of files to be modified.
These .csv files were then reviewed by Greg. Thomas wrote a script to
parse the csv files and add the proper SPDX tag to the file, in the
format that the file expected. This script was further refined by Greg
based on the output to detect more types of files automatically and to
distinguish between header and source .c files (which need different
comment types.) Finally Greg ran the script using the .csv files to
generate the patches.
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-01 14:07:57 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2012-08-07 13:20:46 +00:00
|
|
|
/*
|
|
|
|
* Post mortem Dwarf CFI based unwinding on top of regs and stack dumps.
|
|
|
|
*
|
|
|
|
* Lots of this code have been borrowed or heavily inspired from parts of
|
|
|
|
* the libunwind 0.99 code which are (amongst other contributors I may have
|
|
|
|
* forgotten):
|
|
|
|
*
|
|
|
|
* Copyright (C) 2002-2007 Hewlett-Packard Co
|
|
|
|
* Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
|
|
|
*
|
|
|
|
* And the bugs have been added by:
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
|
|
|
|
* Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <elf.h>
|
2017-04-18 13:46:11 +00:00
|
|
|
#include <errno.h>
|
2012-08-07 13:20:46 +00:00
|
|
|
#include <gelf.h>
|
|
|
|
#include <fcntl.h>
|
2017-04-17 18:23:08 +00:00
|
|
|
#include <inttypes.h>
|
2012-08-07 13:20:46 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <linux/list.h>
|
2019-07-04 15:06:20 +00:00
|
|
|
#include <linux/zalloc.h>
|
2016-06-03 03:33:21 +00:00
|
|
|
#ifndef REMOTE_UNWIND_LIBUNWIND
|
2012-08-07 13:20:46 +00:00
|
|
|
#include <libunwind.h>
|
|
|
|
#include <libunwind-ptrace.h>
|
2016-06-03 03:33:21 +00:00
|
|
|
#endif
|
2014-10-06 00:46:00 +00:00
|
|
|
#include "callchain.h"
|
2012-08-07 13:20:46 +00:00
|
|
|
#include "thread.h"
|
|
|
|
#include "session.h"
|
|
|
|
#include "perf_regs.h"
|
|
|
|
#include "unwind.h"
|
2019-01-27 12:42:37 +00:00
|
|
|
#include "map.h"
|
2014-01-16 09:39:49 +00:00
|
|
|
#include "symbol.h"
|
2014-07-14 21:46:48 +00:00
|
|
|
#include "debug.h"
|
2016-04-07 07:11:12 +00:00
|
|
|
#include "asm/bug.h"
|
2017-01-17 14:50:35 +00:00
|
|
|
#include "dso.h"
|
2012-08-07 13:20:46 +00:00
|
|
|
|
|
|
|
extern int
|
|
|
|
UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
|
|
|
|
unw_word_t ip,
|
|
|
|
unw_dyn_info_t *di,
|
|
|
|
unw_proc_info_t *pi,
|
|
|
|
int need_unwind_info, void *arg);
|
|
|
|
|
|
|
|
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
|
|
|
|
|
2013-09-26 11:36:38 +00:00
|
|
|
extern int
|
|
|
|
UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
|
|
|
|
unw_word_t ip,
|
|
|
|
unw_word_t segbase,
|
|
|
|
const char *obj_name, unw_word_t start,
|
|
|
|
unw_word_t end);
|
|
|
|
|
|
|
|
#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
|
|
|
|
|
2012-08-07 13:20:46 +00:00
|
|
|
#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
|
|
|
|
#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
|
|
|
|
|
|
|
|
/* Pointer-encoding formats: */
|
|
|
|
#define DW_EH_PE_omit 0xff
|
|
|
|
#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
|
|
|
|
#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
|
|
|
|
#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
|
|
|
|
#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
|
|
|
|
#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
|
|
|
|
|
|
|
|
/* Pointer-encoding application: */
|
|
|
|
#define DW_EH_PE_absptr 0x00 /* absolute value */
|
|
|
|
#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following are not documented by LSB v1.3, yet they are used by
|
|
|
|
* GCC, presumably they aren't documented by LSB since they aren't
|
|
|
|
* used on Linux:
|
|
|
|
*/
|
|
|
|
#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
|
|
|
|
#define DW_EH_PE_aligned 0x50 /* aligned pointer */
|
|
|
|
|
|
|
|
/* Flags intentionaly not handled, since they're not needed:
|
|
|
|
* #define DW_EH_PE_indirect 0x80
|
|
|
|
* #define DW_EH_PE_uleb128 0x01
|
|
|
|
* #define DW_EH_PE_udata2 0x02
|
|
|
|
* #define DW_EH_PE_sleb128 0x09
|
|
|
|
* #define DW_EH_PE_sdata2 0x0a
|
|
|
|
* #define DW_EH_PE_textrel 0x20
|
|
|
|
* #define DW_EH_PE_datarel 0x30
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct unwind_info {
|
|
|
|
struct perf_sample *sample;
|
|
|
|
struct machine *machine;
|
|
|
|
struct thread *thread;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define dw_read(ptr, type, end) ({ \
|
|
|
|
type *__p = (type *) ptr; \
|
|
|
|
type __v; \
|
|
|
|
if ((__p + 1) > (type *) end) \
|
|
|
|
return -EINVAL; \
|
|
|
|
__v = *__p++; \
|
|
|
|
ptr = (typeof(ptr)) __p; \
|
|
|
|
__v; \
|
|
|
|
})
|
|
|
|
|
|
|
|
static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
|
|
|
|
u8 encoding)
|
|
|
|
{
|
|
|
|
u8 *cur = *p;
|
|
|
|
*val = 0;
|
|
|
|
|
|
|
|
switch (encoding) {
|
|
|
|
case DW_EH_PE_omit:
|
|
|
|
*val = 0;
|
|
|
|
goto out;
|
|
|
|
case DW_EH_PE_ptr:
|
|
|
|
*val = dw_read(cur, unsigned long, end);
|
|
|
|
goto out;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (encoding & DW_EH_PE_APPL_MASK) {
|
|
|
|
case DW_EH_PE_absptr:
|
|
|
|
break;
|
|
|
|
case DW_EH_PE_pcrel:
|
|
|
|
*val = (unsigned long) cur;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((encoding & 0x07) == 0x00)
|
|
|
|
encoding |= DW_EH_PE_udata4;
|
|
|
|
|
|
|
|
switch (encoding & DW_EH_PE_FORMAT_MASK) {
|
|
|
|
case DW_EH_PE_sdata4:
|
|
|
|
*val += dw_read(cur, s32, end);
|
|
|
|
break;
|
|
|
|
case DW_EH_PE_udata4:
|
|
|
|
*val += dw_read(cur, u32, end);
|
|
|
|
break;
|
|
|
|
case DW_EH_PE_sdata8:
|
|
|
|
*val += dw_read(cur, s64, end);
|
|
|
|
break;
|
|
|
|
case DW_EH_PE_udata8:
|
|
|
|
*val += dw_read(cur, u64, end);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
*p = cur;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define dw_read_encoded_value(ptr, end, enc) ({ \
|
|
|
|
u64 __v; \
|
|
|
|
if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
|
|
|
|
return -EINVAL; \
|
|
|
|
} \
|
|
|
|
__v; \
|
|
|
|
})
|
|
|
|
|
|
|
|
static u64 elf_section_offset(int fd, const char *name)
|
|
|
|
{
|
|
|
|
Elf *elf;
|
|
|
|
GElf_Ehdr ehdr;
|
|
|
|
GElf_Shdr shdr;
|
|
|
|
u64 offset = 0;
|
|
|
|
|
|
|
|
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
|
|
|
|
if (elf == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (gelf_getehdr(elf, &ehdr) == NULL)
|
|
|
|
break;
|
|
|
|
|
2014-01-16 09:39:49 +00:00
|
|
|
if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
|
2012-08-07 13:20:46 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
offset = shdr.sh_offset;
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
elf_end(elf);
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
perf test: Fix dwarf unwind using libunwind.
Perf tool fails to unwind user stack if the event raises in a shared
object. This patch improves tests/dwarf-unwind.c to demonstrate the
problem by utilizing commonly used glibc function "bsearch". If perf is
not statically linked, the testcase will try to unwind a mixed call
trace.
By debugging libunwind I found that there is a bug in unwind-libunwind:
it always passes 0 as segbase to libunwind, cause libunwind unable to
locate debug_frame entry fir first level ip address (I add some more
debugging output into libunwind to make things clear):
>_Uarm_dwarf_find_debug_frame: start_ip = 10be98, end_ip = 10c2a4
>_Uarm_dwarf_find_debug_frame: found debug_frame table `/lib/libc-2.18.so': segbase=0x0, len=7, gp=0x0, table_data=0x449388
>_Uarm_dwarf_search_unwind_table: call lookup:ip = b6cd3bcc, segbase = 0, rel_ip = b6cd3bcc
>lookup: e->start_ip_offset = bcf18 (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 6d314 (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 33d0c (rel_ip = b6cd3bcc)
...
>lookup: e->start_ip_offset = 15d0c (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 15c40 (rel_ip = b6cd3bcc)
>_Uarm_dwarf_search_unwind_table: IP b6cd3bcc inside range b6c12000-b6d4c000, but no explicit unwind info found
>put_rs_cache: unmasking signals/interrupts and releasing lock
>_Uarm_dwarf_step: returning -10
>_Uarm_step: dwarf_step()=-10
This patch passes map->start as segbase to dwarf_find_debug_frame(), so
di will be initialized correctly.
In addition, dso and executable are different when setting segbase. This
patch first check whether the elf is executable, and pass segbase only
for shared object.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1421203007-75799-1-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-01-14 02:36:47 +00:00
|
|
|
#ifndef NO_LIBUNWIND_DEBUG_FRAME
|
|
|
|
static int elf_is_exec(int fd, const char *name)
|
|
|
|
{
|
|
|
|
Elf *elf;
|
|
|
|
GElf_Ehdr ehdr;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
|
|
|
|
if (elf == NULL)
|
|
|
|
return 0;
|
|
|
|
if (gelf_getehdr(elf, &ehdr) == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
retval = (ehdr.e_type == ET_EXEC);
|
|
|
|
|
|
|
|
out:
|
|
|
|
elf_end(elf);
|
|
|
|
pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-08-07 13:20:46 +00:00
|
|
|
struct table_entry {
|
|
|
|
u32 start_ip_offset;
|
|
|
|
u32 fde_offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct eh_frame_hdr {
|
|
|
|
unsigned char version;
|
|
|
|
unsigned char eh_frame_ptr_enc;
|
|
|
|
unsigned char fde_count_enc;
|
|
|
|
unsigned char table_enc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The rest of the header is variable-length and consists of the
|
|
|
|
* following members:
|
|
|
|
*
|
|
|
|
* encoded_t eh_frame_ptr;
|
|
|
|
* encoded_t fde_count;
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* A single encoded pointer should not be more than 8 bytes. */
|
|
|
|
u64 enc[2];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* struct {
|
|
|
|
* encoded_t start_ip;
|
|
|
|
* encoded_t fde_addr;
|
|
|
|
* } binary_search_table[fde_count];
|
|
|
|
*/
|
2020-05-15 17:29:26 +00:00
|
|
|
char data[];
|
2012-08-07 13:20:46 +00:00
|
|
|
} __packed;
|
|
|
|
|
|
|
|
static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
|
|
|
|
u64 offset, u64 *table_data, u64 *segbase,
|
|
|
|
u64 *fde_count)
|
|
|
|
{
|
|
|
|
struct eh_frame_hdr hdr;
|
|
|
|
u8 *enc = (u8 *) &hdr.enc;
|
|
|
|
u8 *end = (u8 *) &hdr.data;
|
|
|
|
ssize_t r;
|
|
|
|
|
|
|
|
r = dso__data_read_offset(dso, machine, offset,
|
|
|
|
(u8 *) &hdr, sizeof(hdr));
|
|
|
|
if (r != sizeof(hdr))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* We dont need eh_frame_ptr, just skip it. */
|
|
|
|
dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
|
|
|
|
|
|
|
|
*fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
|
|
|
|
*segbase = offset;
|
|
|
|
*table_data = (enc - (u8 *) &hdr) + offset;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-26 11:36:38 +00:00
|
|
|
static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
|
|
|
|
u64 *table_data, u64 *segbase,
|
|
|
|
u64 *fde_count)
|
2012-08-07 13:20:46 +00:00
|
|
|
{
|
|
|
|
int ret = -EINVAL, fd;
|
2015-03-13 07:02:56 +00:00
|
|
|
u64 offset = dso->data.eh_frame_hdr_offset;
|
2012-08-07 13:20:46 +00:00
|
|
|
|
2015-01-29 08:07:21 +00:00
|
|
|
if (offset == 0) {
|
2015-05-20 16:03:41 +00:00
|
|
|
fd = dso__data_get_fd(dso, machine);
|
2015-01-29 08:07:21 +00:00
|
|
|
if (fd < 0)
|
|
|
|
return -EINVAL;
|
2012-08-07 13:20:46 +00:00
|
|
|
|
2015-01-29 08:07:21 +00:00
|
|
|
/* Check the .eh_frame section for unwinding info */
|
|
|
|
offset = elf_section_offset(fd, ".eh_frame_hdr");
|
2015-03-13 07:02:56 +00:00
|
|
|
dso->data.eh_frame_hdr_offset = offset;
|
2015-05-20 16:03:41 +00:00
|
|
|
dso__data_put_fd(dso);
|
2015-01-29 08:07:21 +00:00
|
|
|
}
|
2012-08-07 13:20:46 +00:00
|
|
|
|
|
|
|
if (offset)
|
|
|
|
ret = unwind_spec_ehframe(dso, machine, offset,
|
|
|
|
table_data, segbase,
|
|
|
|
fde_count);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-09-26 11:36:38 +00:00
|
|
|
#ifndef NO_LIBUNWIND_DEBUG_FRAME
|
|
|
|
static int read_unwind_spec_debug_frame(struct dso *dso,
|
|
|
|
struct machine *machine, u64 *offset)
|
|
|
|
{
|
2015-01-29 08:07:21 +00:00
|
|
|
int fd;
|
2015-03-13 07:02:56 +00:00
|
|
|
u64 ofs = dso->data.debug_frame_offset;
|
2013-09-26 11:36:38 +00:00
|
|
|
|
2017-01-17 14:50:35 +00:00
|
|
|
/* debug_frame can reside in:
|
|
|
|
* - dso
|
|
|
|
* - debug pointed by symsrc_filename
|
|
|
|
* - gnu_debuglink, which doesn't necessary
|
|
|
|
* has to be pointed by symsrc_filename
|
|
|
|
*/
|
2015-01-29 08:07:21 +00:00
|
|
|
if (ofs == 0) {
|
2015-05-20 16:03:41 +00:00
|
|
|
fd = dso__data_get_fd(dso, machine);
|
2017-01-17 14:50:35 +00:00
|
|
|
if (fd >= 0) {
|
|
|
|
ofs = elf_section_offset(fd, ".debug_frame");
|
|
|
|
dso__data_put_fd(dso);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ofs <= 0) {
|
|
|
|
fd = open(dso->symsrc_filename, O_RDONLY);
|
|
|
|
if (fd >= 0) {
|
|
|
|
ofs = elf_section_offset(fd, ".debug_frame");
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ofs <= 0) {
|
|
|
|
char *debuglink = malloc(PATH_MAX);
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret = dso__read_binary_type_filename(
|
|
|
|
dso, DSO_BINARY_TYPE__DEBUGLINK,
|
|
|
|
machine->root_dir, debuglink, PATH_MAX);
|
|
|
|
if (!ret) {
|
|
|
|
fd = open(debuglink, O_RDONLY);
|
|
|
|
if (fd >= 0) {
|
|
|
|
ofs = elf_section_offset(fd,
|
|
|
|
".debug_frame");
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ofs > 0) {
|
|
|
|
if (dso->symsrc_filename != NULL) {
|
|
|
|
pr_warning(
|
|
|
|
"%s: overwrite symsrc(%s,%s)\n",
|
|
|
|
__func__,
|
|
|
|
dso->symsrc_filename,
|
|
|
|
debuglink);
|
2019-07-04 15:06:20 +00:00
|
|
|
zfree(&dso->symsrc_filename);
|
2017-01-17 14:50:35 +00:00
|
|
|
}
|
|
|
|
dso->symsrc_filename = debuglink;
|
|
|
|
} else {
|
|
|
|
free(debuglink);
|
|
|
|
}
|
|
|
|
}
|
2013-09-26 11:36:38 +00:00
|
|
|
|
2015-03-13 07:02:56 +00:00
|
|
|
dso->data.debug_frame_offset = ofs;
|
2015-01-29 08:07:21 +00:00
|
|
|
}
|
2013-09-26 11:36:38 +00:00
|
|
|
|
2015-01-29 08:07:21 +00:00
|
|
|
*offset = ofs;
|
2013-09-26 11:36:38 +00:00
|
|
|
if (*offset)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-08-07 13:20:46 +00:00
|
|
|
static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
|
|
|
|
{
|
|
|
|
struct addr_location al;
|
2018-04-26 12:34:37 +00:00
|
|
|
return thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al);
|
2012-08-07 13:20:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
|
|
|
int need_unwind_info, void *arg)
|
|
|
|
{
|
|
|
|
struct unwind_info *ui = arg;
|
|
|
|
struct map *map;
|
|
|
|
unw_dyn_info_t di;
|
|
|
|
u64 table_data, segbase, fde_count;
|
2015-09-27 18:37:57 +00:00
|
|
|
int ret = -EINVAL;
|
2012-08-07 13:20:46 +00:00
|
|
|
|
|
|
|
map = find_map(ip, ui);
|
|
|
|
if (!map || !map->dso)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
|
|
|
|
|
2013-09-26 11:36:38 +00:00
|
|
|
/* Check the .eh_frame section for unwinding info */
|
|
|
|
if (!read_unwind_spec_eh_frame(map->dso, ui->machine,
|
|
|
|
&table_data, &segbase, &fde_count)) {
|
|
|
|
memset(&di, 0, sizeof(di));
|
|
|
|
di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
|
|
|
|
di.start_ip = map->start;
|
|
|
|
di.end_ip = map->end;
|
2016-10-13 10:59:39 +00:00
|
|
|
di.u.rti.segbase = map->start + segbase - map->pgoff;
|
|
|
|
di.u.rti.table_data = map->start + table_data - map->pgoff;
|
2013-09-26 11:36:38 +00:00
|
|
|
di.u.rti.table_len = fde_count * sizeof(struct table_entry)
|
|
|
|
/ sizeof(unw_word_t);
|
2015-09-27 18:37:57 +00:00
|
|
|
ret = dwarf_search_unwind_table(as, ip, &di, pi,
|
|
|
|
need_unwind_info, arg);
|
2013-09-26 11:36:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef NO_LIBUNWIND_DEBUG_FRAME
|
|
|
|
/* Check the .debug_frame section for unwinding info */
|
2015-09-27 18:37:57 +00:00
|
|
|
if (ret < 0 &&
|
|
|
|
!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
|
2015-05-20 16:03:41 +00:00
|
|
|
int fd = dso__data_get_fd(map->dso, ui->machine);
|
perf test: Fix dwarf unwind using libunwind.
Perf tool fails to unwind user stack if the event raises in a shared
object. This patch improves tests/dwarf-unwind.c to demonstrate the
problem by utilizing commonly used glibc function "bsearch". If perf is
not statically linked, the testcase will try to unwind a mixed call
trace.
By debugging libunwind I found that there is a bug in unwind-libunwind:
it always passes 0 as segbase to libunwind, cause libunwind unable to
locate debug_frame entry fir first level ip address (I add some more
debugging output into libunwind to make things clear):
>_Uarm_dwarf_find_debug_frame: start_ip = 10be98, end_ip = 10c2a4
>_Uarm_dwarf_find_debug_frame: found debug_frame table `/lib/libc-2.18.so': segbase=0x0, len=7, gp=0x0, table_data=0x449388
>_Uarm_dwarf_search_unwind_table: call lookup:ip = b6cd3bcc, segbase = 0, rel_ip = b6cd3bcc
>lookup: e->start_ip_offset = bcf18 (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 6d314 (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 33d0c (rel_ip = b6cd3bcc)
...
>lookup: e->start_ip_offset = 15d0c (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 15c40 (rel_ip = b6cd3bcc)
>_Uarm_dwarf_search_unwind_table: IP b6cd3bcc inside range b6c12000-b6d4c000, but no explicit unwind info found
>put_rs_cache: unmasking signals/interrupts and releasing lock
>_Uarm_dwarf_step: returning -10
>_Uarm_step: dwarf_step()=-10
This patch passes map->start as segbase to dwarf_find_debug_frame(), so
di will be initialized correctly.
In addition, dso and executable are different when setting segbase. This
patch first check whether the elf is executable, and pass segbase only
for shared object.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1421203007-75799-1-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-01-14 02:36:47 +00:00
|
|
|
int is_exec = elf_is_exec(fd, map->dso->name);
|
|
|
|
unw_word_t base = is_exec ? 0 : map->start;
|
2015-10-29 07:49:36 +00:00
|
|
|
const char *symfile;
|
perf test: Fix dwarf unwind using libunwind.
Perf tool fails to unwind user stack if the event raises in a shared
object. This patch improves tests/dwarf-unwind.c to demonstrate the
problem by utilizing commonly used glibc function "bsearch". If perf is
not statically linked, the testcase will try to unwind a mixed call
trace.
By debugging libunwind I found that there is a bug in unwind-libunwind:
it always passes 0 as segbase to libunwind, cause libunwind unable to
locate debug_frame entry fir first level ip address (I add some more
debugging output into libunwind to make things clear):
>_Uarm_dwarf_find_debug_frame: start_ip = 10be98, end_ip = 10c2a4
>_Uarm_dwarf_find_debug_frame: found debug_frame table `/lib/libc-2.18.so': segbase=0x0, len=7, gp=0x0, table_data=0x449388
>_Uarm_dwarf_search_unwind_table: call lookup:ip = b6cd3bcc, segbase = 0, rel_ip = b6cd3bcc
>lookup: e->start_ip_offset = bcf18 (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 6d314 (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 33d0c (rel_ip = b6cd3bcc)
...
>lookup: e->start_ip_offset = 15d0c (rel_ip = b6cd3bcc)
>lookup: e->start_ip_offset = 15c40 (rel_ip = b6cd3bcc)
>_Uarm_dwarf_search_unwind_table: IP b6cd3bcc inside range b6c12000-b6d4c000, but no explicit unwind info found
>put_rs_cache: unmasking signals/interrupts and releasing lock
>_Uarm_dwarf_step: returning -10
>_Uarm_step: dwarf_step()=-10
This patch passes map->start as segbase to dwarf_find_debug_frame(), so
di will be initialized correctly.
In addition, dso and executable are different when setting segbase. This
patch first check whether the elf is executable, and pass segbase only
for shared object.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1421203007-75799-1-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-01-14 02:36:47 +00:00
|
|
|
|
2015-05-20 16:03:41 +00:00
|
|
|
if (fd >= 0)
|
2015-06-16 11:16:35 +00:00
|
|
|
dso__data_put_fd(map->dso);
|
2015-05-20 16:03:41 +00:00
|
|
|
|
2015-10-29 07:49:36 +00:00
|
|
|
symfile = map->dso->symsrc_filename ?: map->dso->name;
|
|
|
|
|
2013-09-26 11:36:38 +00:00
|
|
|
memset(&di, 0, sizeof(di));
|
2015-10-29 07:49:36 +00:00
|
|
|
if (dwarf_find_debug_frame(0, &di, ip, base, symfile,
|
2013-12-16 16:43:14 +00:00
|
|
|
map->start, map->end))
|
|
|
|
return dwarf_search_unwind_table(as, ip, &di, pi,
|
|
|
|
need_unwind_info, arg);
|
2013-09-26 11:36:38 +00:00
|
|
|
}
|
|
|
|
#endif
|
2012-08-07 13:20:46 +00:00
|
|
|
|
2015-09-27 18:37:57 +00:00
|
|
|
return ret;
|
2012-08-07 13:20:46 +00:00
|
|
|
}
|
|
|
|
|
2012-09-10 22:15:03 +00:00
|
|
|
static int access_fpreg(unw_addr_space_t __maybe_unused as,
|
|
|
|
unw_regnum_t __maybe_unused num,
|
|
|
|
unw_fpreg_t __maybe_unused *val,
|
|
|
|
int __maybe_unused __write,
|
|
|
|
void __maybe_unused *arg)
|
2012-08-07 13:20:46 +00:00
|
|
|
{
|
|
|
|
pr_err("unwind: access_fpreg unsupported\n");
|
|
|
|
return -UNW_EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-09-10 22:15:03 +00:00
|
|
|
static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
|
|
|
|
unw_word_t __maybe_unused *dil_addr,
|
|
|
|
void __maybe_unused *arg)
|
2012-08-07 13:20:46 +00:00
|
|
|
{
|
|
|
|
return -UNW_ENOINFO;
|
|
|
|
}
|
|
|
|
|
2012-09-10 22:15:03 +00:00
|
|
|
static int resume(unw_addr_space_t __maybe_unused as,
|
|
|
|
unw_cursor_t __maybe_unused *cu,
|
|
|
|
void __maybe_unused *arg)
|
2012-08-07 13:20:46 +00:00
|
|
|
{
|
|
|
|
pr_err("unwind: resume unsupported\n");
|
|
|
|
return -UNW_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2012-09-10 22:15:03 +00:00
|
|
|
get_proc_name(unw_addr_space_t __maybe_unused as,
|
|
|
|
unw_word_t __maybe_unused addr,
|
|
|
|
char __maybe_unused *bufp, size_t __maybe_unused buf_len,
|
|
|
|
unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
|
2012-08-07 13:20:46 +00:00
|
|
|
{
|
|
|
|
pr_err("unwind: get_proc_name unsupported\n");
|
|
|
|
return -UNW_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
|
|
|
|
unw_word_t *data)
|
|
|
|
{
|
2016-01-07 09:14:00 +00:00
|
|
|
struct map *map;
|
2012-08-07 13:20:46 +00:00
|
|
|
ssize_t size;
|
|
|
|
|
2016-01-07 09:14:00 +00:00
|
|
|
map = find_map(addr, ui);
|
|
|
|
if (!map) {
|
2012-08-07 13:20:46 +00:00
|
|
|
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-01-07 09:14:00 +00:00
|
|
|
if (!map->dso)
|
2012-08-07 13:20:46 +00:00
|
|
|
return -1;
|
|
|
|
|
2016-01-07 09:14:00 +00:00
|
|
|
size = dso__data_read_addr(map->dso, map, ui->machine,
|
2012-08-07 13:20:46 +00:00
|
|
|
addr, (u8 *) data, sizeof(*data));
|
|
|
|
|
|
|
|
return !(size == sizeof(*data));
|
|
|
|
}
|
|
|
|
|
2012-09-10 22:15:03 +00:00
|
|
|
static int access_mem(unw_addr_space_t __maybe_unused as,
|
2012-08-07 13:20:46 +00:00
|
|
|
unw_word_t addr, unw_word_t *valp,
|
|
|
|
int __write, void *arg)
|
|
|
|
{
|
|
|
|
struct unwind_info *ui = arg;
|
|
|
|
struct stack_dump *stack = &ui->sample->user_stack;
|
2014-01-07 12:47:29 +00:00
|
|
|
u64 start, end;
|
2012-08-07 13:20:46 +00:00
|
|
|
int offset;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Don't support write, probably not needed. */
|
|
|
|
if (__write || !stack || !ui->sample->user_regs.regs) {
|
|
|
|
*valp = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-22 06:57:02 +00:00
|
|
|
ret = perf_reg_value(&start, &ui->sample->user_regs,
|
|
|
|
LIBUNWIND__ARCH_REG_SP);
|
2012-08-07 13:20:46 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
end = start + stack->size;
|
|
|
|
|
|
|
|
/* Check overflow. */
|
|
|
|
if (addr + sizeof(unw_word_t) < addr)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (addr < start || addr + sizeof(unw_word_t) >= end) {
|
|
|
|
ret = access_dso_mem(ui, addr, valp);
|
|
|
|
if (ret) {
|
2014-01-07 12:47:29 +00:00
|
|
|
pr_debug("unwind: access_mem %p not inside range"
|
|
|
|
" 0x%" PRIx64 "-0x%" PRIx64 "\n",
|
2015-09-27 18:37:55 +00:00
|
|
|
(void *) (uintptr_t) addr, start, end);
|
2012-08-07 13:20:46 +00:00
|
|
|
*valp = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = addr - start;
|
|
|
|
*valp = *(unw_word_t *)&stack->data[offset];
|
2014-01-07 12:47:29 +00:00
|
|
|
pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
|
2015-09-27 18:37:55 +00:00
|
|
|
(void *) (uintptr_t) addr, (unsigned long)*valp, offset);
|
2012-08-07 13:20:46 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-10 22:15:03 +00:00
|
|
|
static int access_reg(unw_addr_space_t __maybe_unused as,
|
2012-08-07 13:20:46 +00:00
|
|
|
unw_regnum_t regnum, unw_word_t *valp,
|
|
|
|
int __write, void *arg)
|
|
|
|
{
|
|
|
|
struct unwind_info *ui = arg;
|
|
|
|
int id, ret;
|
2014-01-07 12:47:29 +00:00
|
|
|
u64 val;
|
2012-08-07 13:20:46 +00:00
|
|
|
|
|
|
|
/* Don't support write, I suspect we don't need it. */
|
|
|
|
if (__write) {
|
|
|
|
pr_err("unwind: access_reg w %d\n", regnum);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ui->sample->user_regs.regs) {
|
|
|
|
*valp = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-03 03:33:20 +00:00
|
|
|
id = LIBUNWIND__ARCH_REG_ID(regnum);
|
2012-08-07 13:20:46 +00:00
|
|
|
if (id < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-01-07 12:47:29 +00:00
|
|
|
ret = perf_reg_value(&val, &ui->sample->user_regs, id);
|
2012-08-07 13:20:46 +00:00
|
|
|
if (ret) {
|
|
|
|
pr_err("unwind: can't read reg %d\n", regnum);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-01-07 12:47:29 +00:00
|
|
|
*valp = (unw_word_t) val;
|
2012-08-07 13:20:46 +00:00
|
|
|
pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-10 22:15:03 +00:00
|
|
|
static void put_unwind_info(unw_addr_space_t __maybe_unused as,
|
|
|
|
unw_proc_info_t *pi __maybe_unused,
|
|
|
|
void *arg __maybe_unused)
|
2012-08-07 13:20:46 +00:00
|
|
|
{
|
|
|
|
pr_debug("unwind: put_unwind_info called\n");
|
|
|
|
}
|
|
|
|
|
2014-10-23 15:50:25 +00:00
|
|
|
static int entry(u64 ip, struct thread *thread,
|
2012-08-07 13:20:46 +00:00
|
|
|
unwind_entry_cb_t cb, void *arg)
|
|
|
|
{
|
|
|
|
struct unwind_entry e;
|
|
|
|
struct addr_location al;
|
|
|
|
|
2019-11-04 14:58:21 +00:00
|
|
|
e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
|
|
|
|
e.ip = ip;
|
|
|
|
e.ms.map = al.map;
|
2019-11-26 01:15:35 +00:00
|
|
|
e.ms.maps = al.maps;
|
2012-08-07 13:20:46 +00:00
|
|
|
|
|
|
|
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
|
|
|
|
al.sym ? al.sym->name : "''",
|
|
|
|
ip,
|
|
|
|
al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
|
|
|
|
|
|
|
|
return cb(&e, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void display_error(int err)
|
|
|
|
{
|
|
|
|
switch (err) {
|
|
|
|
case UNW_EINVAL:
|
|
|
|
pr_err("unwind: Only supports local.\n");
|
|
|
|
break;
|
|
|
|
case UNW_EUNSPEC:
|
|
|
|
pr_err("unwind: Unspecified error.\n");
|
|
|
|
break;
|
|
|
|
case UNW_EBADREG:
|
|
|
|
pr_err("unwind: Register unavailable.\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static unw_accessors_t accessors = {
|
|
|
|
.find_proc_info = find_proc_info,
|
|
|
|
.put_unwind_info = put_unwind_info,
|
|
|
|
.get_dyn_info_list_addr = get_dyn_info_list_addr,
|
|
|
|
.access_mem = access_mem,
|
|
|
|
.access_reg = access_reg,
|
|
|
|
.access_fpreg = access_fpreg,
|
|
|
|
.resume = resume,
|
|
|
|
.get_proc_name = get_proc_name,
|
|
|
|
};
|
|
|
|
|
2019-11-26 01:21:28 +00:00
|
|
|
static int _unwind__prepare_access(struct maps *maps)
|
2012-08-07 13:20:46 +00:00
|
|
|
{
|
2019-11-26 01:21:28 +00:00
|
|
|
maps->addr_space = unw_create_addr_space(&accessors, 0);
|
|
|
|
if (!maps->addr_space) {
|
2012-08-07 13:20:46 +00:00
|
|
|
pr_err("unwind: Can't create unwind address space.\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2019-11-26 01:21:28 +00:00
|
|
|
unw_set_caching_policy(maps->addr_space, UNW_CACHE_GLOBAL);
|
2014-10-06 00:46:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-26 01:21:28 +00:00
|
|
|
static void _unwind__flush_access(struct maps *maps)
|
2014-10-06 00:46:01 +00:00
|
|
|
{
|
2019-11-26 01:21:28 +00:00
|
|
|
unw_flush_cache(maps->addr_space, 0, 0);
|
2014-10-06 00:46:01 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 01:21:28 +00:00
|
|
|
static void _unwind__finish_access(struct maps *maps)
|
2014-10-06 00:46:00 +00:00
|
|
|
{
|
2019-11-26 01:21:28 +00:00
|
|
|
unw_destroy_addr_space(maps->addr_space);
|
2014-10-06 00:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
|
|
|
|
void *arg, int max_stack)
|
|
|
|
{
|
2015-11-17 15:05:37 +00:00
|
|
|
u64 val;
|
2015-11-18 07:52:47 +00:00
|
|
|
unw_word_t ips[max_stack];
|
2014-10-06 00:46:00 +00:00
|
|
|
unw_addr_space_t addr_space;
|
|
|
|
unw_cursor_t c;
|
2015-11-18 07:52:47 +00:00
|
|
|
int ret, i = 0;
|
2014-10-06 00:46:00 +00:00
|
|
|
|
2016-06-22 06:57:02 +00:00
|
|
|
ret = perf_reg_value(&val, &ui->sample->user_regs,
|
|
|
|
LIBUNWIND__ARCH_REG_IP);
|
2015-11-17 15:05:37 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2015-11-18 07:52:47 +00:00
|
|
|
ips[i++] = (unw_word_t) val;
|
2015-11-17 15:05:37 +00:00
|
|
|
|
2015-11-18 07:52:47 +00:00
|
|
|
/*
|
|
|
|
* If we need more than one entry, do the DWARF
|
|
|
|
* unwind itself.
|
|
|
|
*/
|
|
|
|
if (max_stack - 1 > 0) {
|
2016-04-07 07:11:12 +00:00
|
|
|
WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
|
2019-11-26 01:07:43 +00:00
|
|
|
addr_space = ui->thread->maps->addr_space;
|
2016-04-07 07:11:12 +00:00
|
|
|
|
2015-11-18 07:52:47 +00:00
|
|
|
if (addr_space == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = unw_init_remote(&c, addr_space, ui);
|
|
|
|
if (ret)
|
|
|
|
display_error(ret);
|
|
|
|
|
|
|
|
while (!ret && (unw_step(&c) > 0) && i < max_stack) {
|
|
|
|
unw_get_reg(&c, UNW_REG_IP, &ips[i]);
|
perf report: Fix off-by-one for non-activation frames
As the documentation for dwfl_frame_pc says, frames that
are no activation frames need to have their program counter
decremented by one to properly find the function of the caller.
This fixes many cases where perf report currently attributes
the cost to the next line. I.e. I have code like this:
~~~~~~~~~~~~~~~
#include <thread>
#include <chrono>
using namespace std;
int main()
{
this_thread::sleep_for(chrono::milliseconds(1000));
this_thread::sleep_for(chrono::milliseconds(100));
this_thread::sleep_for(chrono::milliseconds(10));
return 0;
}
~~~~~~~~~~~~~~~
Now compile and record it:
~~~~~~~~~~~~~~~
g++ -std=c++11 -g -O2 test.cpp
echo 1 | sudo tee /proc/sys/kernel/sched_schedstats
perf record \
--event sched:sched_stat_sleep \
--event sched:sched_process_exit \
--event sched:sched_switch --call-graph=dwarf \
--output perf.data.raw \
./a.out
echo 0 | sudo tee /proc/sys/kernel/sched_schedstats
perf inject --sched-stat --input perf.data.raw --output perf.data
~~~~~~~~~~~~~~~
Before this patch, the report clearly shows the off-by-one issue.
Most notably, the last sleep invocation is incorrectly attributed
to the "return 0;" line:
~~~~~~~~~~~~~~~
Overhead Source:Line
........ ...........
100.00% core.c:0
|
---__schedule core.c:0
schedule
do_nanosleep hrtimer.c:0
hrtimer_nanosleep
sys_nanosleep
entry_SYSCALL_64_fastpath .tmp_entry_64.o:0
__nanosleep_nocancel .:0
std::this_thread::sleep_for<long, std::ratio<1l, 1000l> > thread:323
|
|--90.08%--main test.cpp:9
| __libc_start_main
| _start
|
|--9.01%--main test.cpp:10
| __libc_start_main
| _start
|
--0.91%--main test.cpp:13
__libc_start_main
_start
~~~~~~~~~~~~~~~
With this patch here applied, the issue is fixed. The report becomes
much more usable:
~~~~~~~~~~~~~~~
Overhead Source:Line
........ ...........
100.00% core.c:0
|
---__schedule core.c:0
schedule
do_nanosleep hrtimer.c:0
hrtimer_nanosleep
sys_nanosleep
entry_SYSCALL_64_fastpath .tmp_entry_64.o:0
__nanosleep_nocancel .:0
std::this_thread::sleep_for<long, std::ratio<1l, 1000l> > thread:323
|
|--90.08%--main test.cpp:8
| __libc_start_main
| _start
|
|--9.01%--main test.cpp:9
| __libc_start_main
| _start
|
--0.91%--main test.cpp:10
__libc_start_main
_start
~~~~~~~~~~~~~~~
Similarly it works for signal frames:
~~~~~~~~~~~~~~~
__noinline void bar(void)
{
volatile long cnt = 0;
for (cnt = 0; cnt < 100000000; cnt++);
}
__noinline void foo(void)
{
bar();
}
void sig_handler(int sig)
{
foo();
}
int main(void)
{
signal(SIGUSR1, sig_handler);
raise(SIGUSR1);
foo();
return 0;
}
~~~~~~~~~~~~~~~~
Before, the report wrongly points to `signal.c:29` after raise():
~~~~~~~~~~~~~~~~
$ perf report --stdio --no-children -g srcline -s srcline
...
100.00% signal.c:11
|
---bar signal.c:11
|
|--50.49%--main signal.c:29
| __libc_start_main
| _start
|
--49.51%--0x33a8f
raise .:0
main signal.c:29
__libc_start_main
_start
~~~~~~~~~~~~~~~~
With this patch in, the issue is fixed and we instead get:
~~~~~~~~~~~~~~~~
100.00% signal signal [.] bar
|
---bar signal.c:11
|
|--50.49%--main signal.c:29
| __libc_start_main
| _start
|
--49.51%--0x33a8f
raise .:0
main signal.c:27
__libc_start_main
_start
~~~~~~~~~~~~~~~~
Note how this patch fixes this issue for both unwinding methods, i.e.
both dwfl and libunwind. The former case is straight-forward thanks
to dwfl_frame_pc(). For libunwind, we replace the functionality via
unw_is_signal_frame() for any but the very first frame.
Signed-off-by: Milian Wolff <milian.wolff@kdab.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Yao Jin <yao.jin@linux.intel.com>
Cc: kernel-team@lge.com
Link: http://lkml.kernel.org/r/20170524062129.32529-4-namhyung@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-05-24 06:21:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Decrement the IP for any non-activation frames.
|
|
|
|
* this is required to properly find the srcline
|
|
|
|
* for caller frames.
|
|
|
|
* See also the documentation for dwfl_frame_pc(),
|
|
|
|
* which this code tries to replicate.
|
|
|
|
*/
|
|
|
|
if (unw_is_signal_frame(&c) <= 0)
|
|
|
|
--ips[i];
|
|
|
|
|
2015-11-18 07:52:47 +00:00
|
|
|
++i;
|
|
|
|
}
|
2014-10-06 00:46:00 +00:00
|
|
|
|
2015-11-18 07:52:47 +00:00
|
|
|
max_stack = i;
|
|
|
|
}
|
2012-08-07 13:20:46 +00:00
|
|
|
|
2015-11-18 07:52:47 +00:00
|
|
|
/*
|
|
|
|
* Display what we got based on the order setup.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < max_stack && !ret; i++) {
|
|
|
|
int j = i;
|
2012-08-07 13:20:46 +00:00
|
|
|
|
2015-11-18 07:52:47 +00:00
|
|
|
if (callchain_param.order == ORDER_CALLER)
|
|
|
|
j = max_stack - i - 1;
|
|
|
|
ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
|
2012-08-07 13:20:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-06-03 03:33:12 +00:00
|
|
|
static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
|
2014-10-23 19:42:19 +00:00
|
|
|
struct thread *thread,
|
2014-01-07 12:47:25 +00:00
|
|
|
struct perf_sample *data, int max_stack)
|
2012-08-07 13:20:46 +00:00
|
|
|
{
|
|
|
|
struct unwind_info ui = {
|
|
|
|
.sample = data,
|
|
|
|
.thread = thread,
|
2019-11-26 01:07:43 +00:00
|
|
|
.machine = thread->maps->machine,
|
2012-08-07 13:20:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (!data->user_regs.regs)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-11-17 15:05:37 +00:00
|
|
|
if (max_stack <= 0)
|
|
|
|
return -EINVAL;
|
2012-08-07 13:20:46 +00:00
|
|
|
|
2015-11-17 15:05:37 +00:00
|
|
|
return get_entries(&ui, cb, arg, max_stack);
|
2012-08-07 13:20:46 +00:00
|
|
|
}
|
2016-06-03 03:33:12 +00:00
|
|
|
|
|
|
|
static struct unwind_libunwind_ops
|
|
|
|
_unwind_libunwind_ops = {
|
|
|
|
.prepare_access = _unwind__prepare_access,
|
|
|
|
.flush_access = _unwind__flush_access,
|
|
|
|
.finish_access = _unwind__finish_access,
|
|
|
|
.get_entries = _unwind__get_entries,
|
|
|
|
};
|
|
|
|
|
2016-06-03 03:33:21 +00:00
|
|
|
#ifndef REMOTE_UNWIND_LIBUNWIND
|
2016-06-03 03:33:12 +00:00
|
|
|
struct unwind_libunwind_ops *
|
|
|
|
local_unwind_libunwind_ops = &_unwind_libunwind_ops;
|
2016-06-03 03:33:21 +00:00
|
|
|
#endif
|