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 22:07:57 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2014-04-30 21:25:10 +08:00
|
|
|
#include <asm/bug.h>
|
2017-04-17 22:39:06 +08:00
|
|
|
#include <linux/kernel.h>
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/resource.h>
|
2017-04-20 07:57:47 +08:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2017-04-18 21:46:11 +08:00
|
|
|
#include <errno.h>
|
2017-09-11 21:50:26 +08:00
|
|
|
#include <fcntl.h>
|
2017-04-20 03:29:38 +08:00
|
|
|
#include "compress.h"
|
2017-04-18 22:33:48 +08:00
|
|
|
#include "path.h"
|
2012-10-28 05:18:32 +08:00
|
|
|
#include "symbol.h"
|
2017-10-10 04:32:59 +08:00
|
|
|
#include "srcline.h"
|
2012-10-28 05:18:32 +08:00
|
|
|
#include "dso.h"
|
2012-11-09 22:32:52 +08:00
|
|
|
#include "machine.h"
|
2015-04-09 23:53:55 +08:00
|
|
|
#include "auxtrace.h"
|
2012-10-28 05:18:32 +08:00
|
|
|
#include "util.h"
|
|
|
|
#include "debug.h"
|
2017-04-18 03:51:59 +08:00
|
|
|
#include "string2.h"
|
2016-05-12 16:43:11 +08:00
|
|
|
#include "vdso.h"
|
2012-10-28 05:18:32 +08:00
|
|
|
|
2017-01-17 22:50:35 +08:00
|
|
|
static const char * const debuglink_paths[] = {
|
|
|
|
"%.0s%s",
|
|
|
|
"%s/%s",
|
|
|
|
"%s/.debug/%s",
|
|
|
|
"/usr/lib/debug%s/%s"
|
|
|
|
};
|
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
char dso__symtab_origin(const struct dso *dso)
|
|
|
|
{
|
|
|
|
static const char origin[] = {
|
2013-09-18 21:56:14 +08:00
|
|
|
[DSO_BINARY_TYPE__KALLSYMS] = 'k',
|
|
|
|
[DSO_BINARY_TYPE__VMLINUX] = 'v',
|
|
|
|
[DSO_BINARY_TYPE__JAVA_JIT] = 'j',
|
|
|
|
[DSO_BINARY_TYPE__DEBUGLINK] = 'l',
|
|
|
|
[DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B',
|
2017-07-06 09:48:13 +08:00
|
|
|
[DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO] = 'D',
|
2013-09-18 21:56:14 +08:00
|
|
|
[DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f',
|
|
|
|
[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u',
|
|
|
|
[DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO] = 'o',
|
|
|
|
[DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b',
|
|
|
|
[DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd',
|
|
|
|
[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K',
|
2014-11-04 09:14:27 +08:00
|
|
|
[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP] = 'm',
|
2013-09-18 21:56:14 +08:00
|
|
|
[DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g',
|
|
|
|
[DSO_BINARY_TYPE__GUEST_KMODULE] = 'G',
|
2014-11-04 09:14:27 +08:00
|
|
|
[DSO_BINARY_TYPE__GUEST_KMODULE_COMP] = 'M',
|
2013-09-18 21:56:14 +08:00
|
|
|
[DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V',
|
2012-10-28 05:18:32 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND)
|
|
|
|
return '!';
|
|
|
|
return origin[dso->symtab_type];
|
|
|
|
}
|
|
|
|
|
2013-12-17 04:03:18 +08:00
|
|
|
int dso__read_binary_type_filename(const struct dso *dso,
|
|
|
|
enum dso_binary_type type,
|
|
|
|
char *root_dir, char *filename, size_t size)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2016-05-11 21:51:59 +08:00
|
|
|
char build_id_hex[SBUILD_ID_SIZE];
|
2012-10-28 05:18:32 +08:00
|
|
|
int ret = 0;
|
2014-07-29 21:21:58 +08:00
|
|
|
size_t len;
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
switch (type) {
|
2017-01-17 22:50:35 +08:00
|
|
|
case DSO_BINARY_TYPE__DEBUGLINK:
|
|
|
|
{
|
|
|
|
const char *last_slash;
|
|
|
|
char dso_dir[PATH_MAX];
|
|
|
|
char symfile[PATH_MAX];
|
|
|
|
unsigned int i;
|
2012-10-28 05:18:32 +08:00
|
|
|
|
2015-01-27 14:34:02 +08:00
|
|
|
len = __symbol__join_symfs(filename, size, dso->long_name);
|
2017-01-17 22:50:35 +08:00
|
|
|
last_slash = filename + len;
|
|
|
|
while (last_slash != filename && *last_slash != '/')
|
|
|
|
last_slash--;
|
2016-01-20 19:56:32 +08:00
|
|
|
|
2017-01-17 22:50:35 +08:00
|
|
|
strncpy(dso_dir, filename, last_slash - filename);
|
|
|
|
dso_dir[last_slash-filename] = '\0';
|
|
|
|
|
|
|
|
if (!is_regular_file(filename)) {
|
|
|
|
ret = -1;
|
2016-01-20 19:56:32 +08:00
|
|
|
break;
|
2017-01-17 22:50:35 +08:00
|
|
|
}
|
2016-01-20 19:56:32 +08:00
|
|
|
|
2017-01-17 22:50:35 +08:00
|
|
|
ret = filename__read_debuglink(filename, symfile, PATH_MAX);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Check predefined locations where debug file might reside */
|
|
|
|
ret = -1;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(debuglink_paths); i++) {
|
|
|
|
snprintf(filename, size,
|
|
|
|
debuglink_paths[i], dso_dir, symfile);
|
|
|
|
if (is_regular_file(filename)) {
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
2017-01-17 22:50:35 +08:00
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
break;
|
2017-01-17 22:50:35 +08:00
|
|
|
}
|
2012-10-28 05:18:32 +08:00
|
|
|
case DSO_BINARY_TYPE__BUILD_ID_CACHE:
|
2017-07-06 09:48:13 +08:00
|
|
|
if (dso__build_id_filename(dso, filename, size, false) == NULL)
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
|
|
|
|
if (dso__build_id_filename(dso, filename, size, true) == NULL)
|
2012-10-28 05:18:32 +08:00
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
|
2014-07-29 21:21:58 +08:00
|
|
|
len = __symbol__join_symfs(filename, size, "/usr/lib/debug");
|
|
|
|
snprintf(filename + len, size - len, "%s.debug", dso->long_name);
|
2012-10-28 05:18:32 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
|
2014-07-29 21:21:58 +08:00
|
|
|
len = __symbol__join_symfs(filename, size, "/usr/lib/debug");
|
|
|
|
snprintf(filename + len, size - len, "%s", dso->long_name);
|
2012-10-28 05:18:32 +08:00
|
|
|
break;
|
|
|
|
|
2013-09-18 21:56:14 +08:00
|
|
|
case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
|
|
|
|
{
|
2013-12-11 02:19:23 +08:00
|
|
|
const char *last_slash;
|
2013-09-18 21:56:14 +08:00
|
|
|
size_t dir_size;
|
|
|
|
|
|
|
|
last_slash = dso->long_name + dso->long_name_len;
|
|
|
|
while (last_slash != dso->long_name && *last_slash != '/')
|
|
|
|
last_slash--;
|
|
|
|
|
2014-07-29 21:21:58 +08:00
|
|
|
len = __symbol__join_symfs(filename, size, "");
|
2013-09-18 21:56:14 +08:00
|
|
|
dir_size = last_slash - dso->long_name + 2;
|
|
|
|
if (dir_size > (size - len)) {
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
2013-12-11 03:02:50 +08:00
|
|
|
len += scnprintf(filename + len, dir_size, "%s", dso->long_name);
|
|
|
|
len += scnprintf(filename + len , size - len, ".debug%s",
|
2013-09-18 21:56:14 +08:00
|
|
|
last_slash);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
|
|
|
|
if (!dso->has_build_id) {
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
build_id__sprintf(dso->build_id,
|
|
|
|
sizeof(dso->build_id),
|
|
|
|
build_id_hex);
|
2014-07-29 21:21:58 +08:00
|
|
|
len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/");
|
|
|
|
snprintf(filename + len, size - len, "%.2s/%s.debug",
|
|
|
|
build_id_hex, build_id_hex + 2);
|
2012-10-28 05:18:32 +08:00
|
|
|
break;
|
|
|
|
|
2013-08-07 19:38:47 +08:00
|
|
|
case DSO_BINARY_TYPE__VMLINUX:
|
|
|
|
case DSO_BINARY_TYPE__GUEST_VMLINUX:
|
2012-10-28 05:18:32 +08:00
|
|
|
case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
|
2014-07-29 21:21:58 +08:00
|
|
|
__symbol__join_symfs(filename, size, dso->long_name);
|
2012-10-28 05:18:32 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DSO_BINARY_TYPE__GUEST_KMODULE:
|
2014-11-04 09:14:27 +08:00
|
|
|
case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
|
2014-07-29 21:21:58 +08:00
|
|
|
path__join3(filename, size, symbol_conf.symfs,
|
|
|
|
root_dir, dso->long_name);
|
2012-10-28 05:18:32 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
|
2014-11-04 09:14:27 +08:00
|
|
|
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
|
2014-07-29 21:21:58 +08:00
|
|
|
__symbol__join_symfs(filename, size, dso->long_name);
|
2012-10-28 05:18:32 +08:00
|
|
|
break;
|
|
|
|
|
2013-08-07 19:38:51 +08:00
|
|
|
case DSO_BINARY_TYPE__KCORE:
|
|
|
|
case DSO_BINARY_TYPE__GUEST_KCORE:
|
2013-12-11 03:02:50 +08:00
|
|
|
snprintf(filename, size, "%s", dso->long_name);
|
2013-08-07 19:38:51 +08:00
|
|
|
break;
|
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
default:
|
|
|
|
case DSO_BINARY_TYPE__KALLSYMS:
|
|
|
|
case DSO_BINARY_TYPE__GUEST_KALLSYMS:
|
|
|
|
case DSO_BINARY_TYPE__JAVA_JIT:
|
|
|
|
case DSO_BINARY_TYPE__NOT_FOUND:
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-11-04 09:14:27 +08:00
|
|
|
static const struct {
|
|
|
|
const char *fmt;
|
|
|
|
int (*decompress)(const char *input, int output);
|
|
|
|
} compressions[] = {
|
perf tools: Add gzip decompression support for kernel module
Now my Archlinux box shows module symbols correctly.
Before:
$ perf report --stdio
Failed to open /tmp/perf-3477.map, continuing without symbols
no symbols found in /usr/bin/date, maybe install a debug package?
No kallsyms or vmlinux with build-id 7b4ea0a49ae2111925857099aaf05c3246ff33e0 was found
[drm] with build id 7b4ea0a49ae2111925857099aaf05c3246ff33e0 not found, continuing without symbols
No kallsyms or vmlinux with build-id edd931629094b660ca9dec09a1b635c8d87aa2ee was found
[jbd2] with build id edd931629094b660ca9dec09a1b635c8d87aa2ee not found, continuing without symbols
No kallsyms or vmlinux with build-id a7b1eada671c34933e5610bb920b2ca4945a82c3 was found
[ext4] with build id a7b1eada671c34933e5610bb920b2ca4945a82c3 not found, continuing without symbols
No kallsyms or vmlinux with build-id d69511fa3e5840e770336ef45b06c83fef8d74e3 was found
[scsi_mod] with build id d69511fa3e5840e770336ef45b06c83fef8d74e3 not found, continuing without symbols
No kallsyms or vmlinux with build-id af0430af13461af058770ee9b87afc07922c2e77 was found
[libata] with build id af0430af13461af058770ee9b87afc07922c2e77 not found, continuing without symbols
No kallsyms or vmlinux with build-id aaeedff8160ce631a5f0333591c6ff291201d29f was found
[libahci] with build id aaeedff8160ce631a5f0333591c6ff291201d29f not found, continuing without symbols
No kallsyms or vmlinux with build-id c57907712becaf662dc4981824bb372c0441d605 was found
[mac80211] with build id c57907712becaf662dc4981824bb372c0441d605 not found, continuing without symbols
No kallsyms or vmlinux with build-id e0589077cc0ec8c3e4c40eb9f2d9e69d236bee8f was found
[iwldvm] with build id e0589077cc0ec8c3e4c40eb9f2d9e69d236bee8f not found, continuing without symbols
No kallsyms or vmlinux with build-id 2d86086bf136bf374a2f029cf85a48194f9b950b was found
[cfg80211] with build id 2d86086bf136bf374a2f029cf85a48194f9b950b not found, continuing without symbols
No kallsyms or vmlinux with build-id 4493c48599bdb3d91d0f8db5150e0be33fdd9221 was found
[iwlwifi] with build id 4493c48599bdb3d91d0f8db5150e0be33fdd9221 not found, continuing without symbols
...
#
# Overhead Command Shared Object Symbol
# ........ ............... ....................... ........................................................
#
0.03% swapper [ext4] [k] 0x000000000000fe2e
0.03% swapper [kernel.kallsyms] [k] account_entity_enqueue
0.03% swapper [ext4] [k] 0x000000000000fc2b
0.03% irq/50-iwlwifi [iwlwifi] [k] 0x000000000000200b
0.03% swapper [kernel.kallsyms] [k] ktime_add_safe
0.03% swapper [kernel.kallsyms] [k] elv_completed_request
0.03% swapper [libata] [k] 0x0000000000003997
0.03% swapper [libahci] [k] 0x0000000000001f25
0.03% swapper [kernel.kallsyms] [k] rb_next
0.03% swapper [kernel.kallsyms] [k] blk_finish_request
0.03% swapper [ext4] [k] 0x0000000000010248
0.00% perf [kernel.kallsyms] [k] native_write_msr_safe
After:
$ perf report --stdio
Failed to open /tmp/perf-3477.map, continuing without symbols
no symbols found in /usr/bin/tr, maybe install a debug package?
...
#
# Overhead Command Shared Object Symbol
# ........ ............... ........................... ......................................................
#
0.04% kworker/u16:3 [ext4] [k] ext4_read_block_bitmap
0.03% kworker/u16:0 [mac80211] [k] ieee80211_sta_reset_beacon_monitor
0.02% irq/50-iwlwifi [mac80211] [k] ieee80211_get_bssid
0.02% firefox [e1000e] [k] __ew32_prepare
0.02% swapper [libahci] [k] ahci_handle_port_interrupt
0.02% emacs libglib-2.0.so.0.4000.0 [.] g_mutex_unlock
0.02% swapper [e1000e] [k] e1000_clean_tx_irq
0.02% dwm [kernel.kallsyms] [k] __schedule
0.02% gnome-terminal- [vdso] [.] __vdso_clock_gettime
0.02% swapper [e1000e] [k] e1000_alloc_rx_buffers
0.02% irq/50-iwlwifi [mac80211] [k] ieee80211_rx
0.01% firefox [vdso] [.] __vdso_gettimeofday
0.01% irq/50-iwlwifi [iwlwifi] [k] iwl_pcie_rxq_restock.part.13
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Namhyung Kim <namhyung.kim@lge.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/87h9yexshi.fsf@sejong.aot.lge.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2014-10-31 15:51:38 +08:00
|
|
|
#ifdef HAVE_ZLIB_SUPPORT
|
|
|
|
{ "gz", gzip_decompress_to_file },
|
2015-01-29 20:29:39 +08:00
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LZMA_SUPPORT
|
|
|
|
{ "xz", lzma_decompress_to_file },
|
perf tools: Add gzip decompression support for kernel module
Now my Archlinux box shows module symbols correctly.
Before:
$ perf report --stdio
Failed to open /tmp/perf-3477.map, continuing without symbols
no symbols found in /usr/bin/date, maybe install a debug package?
No kallsyms or vmlinux with build-id 7b4ea0a49ae2111925857099aaf05c3246ff33e0 was found
[drm] with build id 7b4ea0a49ae2111925857099aaf05c3246ff33e0 not found, continuing without symbols
No kallsyms or vmlinux with build-id edd931629094b660ca9dec09a1b635c8d87aa2ee was found
[jbd2] with build id edd931629094b660ca9dec09a1b635c8d87aa2ee not found, continuing without symbols
No kallsyms or vmlinux with build-id a7b1eada671c34933e5610bb920b2ca4945a82c3 was found
[ext4] with build id a7b1eada671c34933e5610bb920b2ca4945a82c3 not found, continuing without symbols
No kallsyms or vmlinux with build-id d69511fa3e5840e770336ef45b06c83fef8d74e3 was found
[scsi_mod] with build id d69511fa3e5840e770336ef45b06c83fef8d74e3 not found, continuing without symbols
No kallsyms or vmlinux with build-id af0430af13461af058770ee9b87afc07922c2e77 was found
[libata] with build id af0430af13461af058770ee9b87afc07922c2e77 not found, continuing without symbols
No kallsyms or vmlinux with build-id aaeedff8160ce631a5f0333591c6ff291201d29f was found
[libahci] with build id aaeedff8160ce631a5f0333591c6ff291201d29f not found, continuing without symbols
No kallsyms or vmlinux with build-id c57907712becaf662dc4981824bb372c0441d605 was found
[mac80211] with build id c57907712becaf662dc4981824bb372c0441d605 not found, continuing without symbols
No kallsyms or vmlinux with build-id e0589077cc0ec8c3e4c40eb9f2d9e69d236bee8f was found
[iwldvm] with build id e0589077cc0ec8c3e4c40eb9f2d9e69d236bee8f not found, continuing without symbols
No kallsyms or vmlinux with build-id 2d86086bf136bf374a2f029cf85a48194f9b950b was found
[cfg80211] with build id 2d86086bf136bf374a2f029cf85a48194f9b950b not found, continuing without symbols
No kallsyms or vmlinux with build-id 4493c48599bdb3d91d0f8db5150e0be33fdd9221 was found
[iwlwifi] with build id 4493c48599bdb3d91d0f8db5150e0be33fdd9221 not found, continuing without symbols
...
#
# Overhead Command Shared Object Symbol
# ........ ............... ....................... ........................................................
#
0.03% swapper [ext4] [k] 0x000000000000fe2e
0.03% swapper [kernel.kallsyms] [k] account_entity_enqueue
0.03% swapper [ext4] [k] 0x000000000000fc2b
0.03% irq/50-iwlwifi [iwlwifi] [k] 0x000000000000200b
0.03% swapper [kernel.kallsyms] [k] ktime_add_safe
0.03% swapper [kernel.kallsyms] [k] elv_completed_request
0.03% swapper [libata] [k] 0x0000000000003997
0.03% swapper [libahci] [k] 0x0000000000001f25
0.03% swapper [kernel.kallsyms] [k] rb_next
0.03% swapper [kernel.kallsyms] [k] blk_finish_request
0.03% swapper [ext4] [k] 0x0000000000010248
0.00% perf [kernel.kallsyms] [k] native_write_msr_safe
After:
$ perf report --stdio
Failed to open /tmp/perf-3477.map, continuing without symbols
no symbols found in /usr/bin/tr, maybe install a debug package?
...
#
# Overhead Command Shared Object Symbol
# ........ ............... ........................... ......................................................
#
0.04% kworker/u16:3 [ext4] [k] ext4_read_block_bitmap
0.03% kworker/u16:0 [mac80211] [k] ieee80211_sta_reset_beacon_monitor
0.02% irq/50-iwlwifi [mac80211] [k] ieee80211_get_bssid
0.02% firefox [e1000e] [k] __ew32_prepare
0.02% swapper [libahci] [k] ahci_handle_port_interrupt
0.02% emacs libglib-2.0.so.0.4000.0 [.] g_mutex_unlock
0.02% swapper [e1000e] [k] e1000_clean_tx_irq
0.02% dwm [kernel.kallsyms] [k] __schedule
0.02% gnome-terminal- [vdso] [.] __vdso_clock_gettime
0.02% swapper [e1000e] [k] e1000_alloc_rx_buffers
0.02% irq/50-iwlwifi [mac80211] [k] ieee80211_rx
0.01% firefox [vdso] [.] __vdso_gettimeofday
0.01% irq/50-iwlwifi [iwlwifi] [k] iwl_pcie_rxq_restock.part.13
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Namhyung Kim <namhyung.kim@lge.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/87h9yexshi.fsf@sejong.aot.lge.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2014-10-31 15:51:38 +08:00
|
|
|
#endif
|
|
|
|
{ NULL, NULL },
|
2014-11-04 09:14:27 +08:00
|
|
|
};
|
|
|
|
|
2018-08-17 17:48:05 +08:00
|
|
|
static bool is_supported_compression(const char *ext)
|
2014-11-04 09:14:27 +08:00
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; compressions[i].fmt; i++) {
|
|
|
|
if (!strcmp(ext, compressions[i].fmt))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-03 16:52:21 +08:00
|
|
|
bool is_kernel_module(const char *pathname, int cpumode)
|
2014-11-04 09:14:27 +08:00
|
|
|
{
|
2015-02-12 22:56:21 +08:00
|
|
|
struct kmod_path m;
|
2015-06-03 16:52:21 +08:00
|
|
|
int mode = cpumode & PERF_RECORD_MISC_CPUMODE_MASK;
|
|
|
|
|
|
|
|
WARN_ONCE(mode != cpumode,
|
|
|
|
"Internal error: passing unmasked cpumode (%x) to is_kernel_module",
|
|
|
|
cpumode);
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case PERF_RECORD_MISC_USER:
|
|
|
|
case PERF_RECORD_MISC_HYPERVISOR:
|
|
|
|
case PERF_RECORD_MISC_GUEST_USER:
|
|
|
|
return false;
|
|
|
|
/* Treat PERF_RECORD_MISC_CPUMODE_UNKNOWN as kernel */
|
|
|
|
default:
|
|
|
|
if (kmod_path__parse(&m, pathname)) {
|
|
|
|
pr_err("Failed to check whether %s is a kernel module or not. Assume it is.",
|
|
|
|
pathname);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2014-11-04 09:14:27 +08:00
|
|
|
|
2015-02-12 22:56:21 +08:00
|
|
|
return m.kmod;
|
2014-11-04 09:14:27 +08:00
|
|
|
}
|
|
|
|
|
2018-08-17 17:48:04 +08:00
|
|
|
static bool decompress_to_file(const char *ext, const char *filename, int output_fd)
|
2014-11-04 09:14:27 +08:00
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; compressions[i].fmt; i++) {
|
|
|
|
if (!strcmp(ext, compressions[i].fmt))
|
|
|
|
return !compressions[i].decompress(filename,
|
|
|
|
output_fd);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dso__needs_decompress(struct dso *dso)
|
|
|
|
{
|
|
|
|
return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
|
|
|
|
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;
|
|
|
|
}
|
|
|
|
|
2017-06-08 15:31:03 +08:00
|
|
|
static int decompress_kmodule(struct dso *dso, const char *name, char *tmpbuf)
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
struct kmod_path m;
|
|
|
|
|
|
|
|
if (!dso__needs_decompress(dso))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (kmod_path__parse_ext(&m, dso->long_name))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!m.comp)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
fd = mkstemp(tmpbuf);
|
|
|
|
if (fd < 0) {
|
|
|
|
dso->load_errno = errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!decompress_to_file(m.ext, name, fd)) {
|
|
|
|
dso->load_errno = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE;
|
|
|
|
close(fd);
|
|
|
|
fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(m.ext);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dso__decompress_kmodule_fd(struct dso *dso, const char *name)
|
|
|
|
{
|
|
|
|
char tmpbuf[] = KMOD_DECOMP_NAME;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = decompress_kmodule(dso, name, tmpbuf);
|
|
|
|
unlink(tmpbuf);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dso__decompress_kmodule_path(struct dso *dso, const char *name,
|
|
|
|
char *pathname, size_t len)
|
|
|
|
{
|
|
|
|
char tmpbuf[] = KMOD_DECOMP_NAME;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = decompress_kmodule(dso, name, tmpbuf);
|
|
|
|
if (fd < 0) {
|
|
|
|
unlink(tmpbuf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(pathname, tmpbuf, len);
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
perf tools: Add kmod_path__parse function
Provides united way of parsing kernel module path
into several components.
The new kmod_path__parse function and few defines:
int __kmod_path__parse(struct kmod_path *m, const char *path,
bool alloc_name, bool alloc_ext);
#define kmod_path__parse(__m, __p) __kmod_path__parse(__m, __p, false, false)
#define kmod_path__parse_name(__m, __p) __kmod_path__parse(__m, __p, true , false)
#define kmod_path__parse_ext(__m, __p) __kmod_path__parse(__m, __p, false, true)
parse kernel module @path and updates @m argument like:
@comp - true if @path contains supported compression suffix,
false otherwise
@kmod - true if @path contains '.ko' suffix in right position,
false otherwise
@name - if (@alloc_name && @kmod) is true, it contains strdup-ed base name
of the kernel module without suffixes, otherwise strudup-ed
base name of @path
@ext - if (@alloc_ext && @comp) is true, it contains strdup-ed string
the compression suffix
It returns 0 if there's no strdup error, -ENOMEM otherwise.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-9t6eqg8j610r94l743hkntiv@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-02-05 22:40:25 +08:00
|
|
|
/*
|
|
|
|
* Parses kernel module specified in @path and updates
|
|
|
|
* @m argument like:
|
|
|
|
*
|
|
|
|
* @comp - true if @path contains supported compression suffix,
|
|
|
|
* false otherwise
|
|
|
|
* @kmod - true if @path contains '.ko' suffix in right position,
|
|
|
|
* false otherwise
|
|
|
|
* @name - if (@alloc_name && @kmod) is true, it contains strdup-ed base name
|
|
|
|
* of the kernel module without suffixes, otherwise strudup-ed
|
|
|
|
* base name of @path
|
|
|
|
* @ext - if (@alloc_ext && @comp) is true, it contains strdup-ed string
|
|
|
|
* the compression suffix
|
|
|
|
*
|
|
|
|
* Returns 0 if there's no strdup error, -ENOMEM otherwise.
|
|
|
|
*/
|
|
|
|
int __kmod_path__parse(struct kmod_path *m, const char *path,
|
|
|
|
bool alloc_name, bool alloc_ext)
|
|
|
|
{
|
|
|
|
const char *name = strrchr(path, '/');
|
|
|
|
const char *ext = strrchr(path, '.');
|
2015-06-03 16:52:21 +08:00
|
|
|
bool is_simple_name = false;
|
perf tools: Add kmod_path__parse function
Provides united way of parsing kernel module path
into several components.
The new kmod_path__parse function and few defines:
int __kmod_path__parse(struct kmod_path *m, const char *path,
bool alloc_name, bool alloc_ext);
#define kmod_path__parse(__m, __p) __kmod_path__parse(__m, __p, false, false)
#define kmod_path__parse_name(__m, __p) __kmod_path__parse(__m, __p, true , false)
#define kmod_path__parse_ext(__m, __p) __kmod_path__parse(__m, __p, false, true)
parse kernel module @path and updates @m argument like:
@comp - true if @path contains supported compression suffix,
false otherwise
@kmod - true if @path contains '.ko' suffix in right position,
false otherwise
@name - if (@alloc_name && @kmod) is true, it contains strdup-ed base name
of the kernel module without suffixes, otherwise strudup-ed
base name of @path
@ext - if (@alloc_ext && @comp) is true, it contains strdup-ed string
the compression suffix
It returns 0 if there's no strdup error, -ENOMEM otherwise.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-9t6eqg8j610r94l743hkntiv@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-02-05 22:40:25 +08:00
|
|
|
|
|
|
|
memset(m, 0x0, sizeof(*m));
|
|
|
|
name = name ? name + 1 : path;
|
|
|
|
|
2015-06-03 16:52:21 +08:00
|
|
|
/*
|
|
|
|
* '.' is also a valid character for module name. For example:
|
|
|
|
* [aaa.bbb] is a valid module name. '[' should have higher
|
|
|
|
* priority than '.ko' suffix.
|
|
|
|
*
|
|
|
|
* The kernel names are from machine__mmap_name. Such
|
|
|
|
* name should belong to kernel itself, not kernel module.
|
|
|
|
*/
|
|
|
|
if (name[0] == '[') {
|
|
|
|
is_simple_name = true;
|
|
|
|
if ((strncmp(name, "[kernel.kallsyms]", 17) == 0) ||
|
|
|
|
(strncmp(name, "[guest.kernel.kallsyms", 22) == 0) ||
|
|
|
|
(strncmp(name, "[vdso]", 6) == 0) ||
|
2018-06-04 20:56:54 +08:00
|
|
|
(strncmp(name, "[vdso32]", 8) == 0) ||
|
|
|
|
(strncmp(name, "[vdsox32]", 9) == 0) ||
|
2015-06-03 16:52:21 +08:00
|
|
|
(strncmp(name, "[vsyscall]", 10) == 0)) {
|
|
|
|
m->kmod = false;
|
|
|
|
|
|
|
|
} else
|
|
|
|
m->kmod = true;
|
|
|
|
}
|
|
|
|
|
perf tools: Add kmod_path__parse function
Provides united way of parsing kernel module path
into several components.
The new kmod_path__parse function and few defines:
int __kmod_path__parse(struct kmod_path *m, const char *path,
bool alloc_name, bool alloc_ext);
#define kmod_path__parse(__m, __p) __kmod_path__parse(__m, __p, false, false)
#define kmod_path__parse_name(__m, __p) __kmod_path__parse(__m, __p, true , false)
#define kmod_path__parse_ext(__m, __p) __kmod_path__parse(__m, __p, false, true)
parse kernel module @path and updates @m argument like:
@comp - true if @path contains supported compression suffix,
false otherwise
@kmod - true if @path contains '.ko' suffix in right position,
false otherwise
@name - if (@alloc_name && @kmod) is true, it contains strdup-ed base name
of the kernel module without suffixes, otherwise strudup-ed
base name of @path
@ext - if (@alloc_ext && @comp) is true, it contains strdup-ed string
the compression suffix
It returns 0 if there's no strdup error, -ENOMEM otherwise.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-9t6eqg8j610r94l743hkntiv@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-02-05 22:40:25 +08:00
|
|
|
/* No extension, just return name. */
|
2015-06-03 16:52:21 +08:00
|
|
|
if ((ext == NULL) || is_simple_name) {
|
perf tools: Add kmod_path__parse function
Provides united way of parsing kernel module path
into several components.
The new kmod_path__parse function and few defines:
int __kmod_path__parse(struct kmod_path *m, const char *path,
bool alloc_name, bool alloc_ext);
#define kmod_path__parse(__m, __p) __kmod_path__parse(__m, __p, false, false)
#define kmod_path__parse_name(__m, __p) __kmod_path__parse(__m, __p, true , false)
#define kmod_path__parse_ext(__m, __p) __kmod_path__parse(__m, __p, false, true)
parse kernel module @path and updates @m argument like:
@comp - true if @path contains supported compression suffix,
false otherwise
@kmod - true if @path contains '.ko' suffix in right position,
false otherwise
@name - if (@alloc_name && @kmod) is true, it contains strdup-ed base name
of the kernel module without suffixes, otherwise strudup-ed
base name of @path
@ext - if (@alloc_ext && @comp) is true, it contains strdup-ed string
the compression suffix
It returns 0 if there's no strdup error, -ENOMEM otherwise.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-9t6eqg8j610r94l743hkntiv@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-02-05 22:40:25 +08:00
|
|
|
if (alloc_name) {
|
|
|
|
m->name = strdup(name);
|
|
|
|
return m->name ? 0 : -ENOMEM;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_supported_compression(ext + 1)) {
|
|
|
|
m->comp = true;
|
|
|
|
ext -= 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check .ko extension only if there's enough name left. */
|
|
|
|
if (ext > name)
|
|
|
|
m->kmod = !strncmp(ext, ".ko", 3);
|
|
|
|
|
|
|
|
if (alloc_name) {
|
|
|
|
if (m->kmod) {
|
|
|
|
if (asprintf(&m->name, "[%.*s]", (int) (ext - name), name) == -1)
|
|
|
|
return -ENOMEM;
|
|
|
|
} else {
|
|
|
|
if (asprintf(&m->name, "%s", name) == -1)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
strxfrchar(m->name, '-', '_');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alloc_ext && m->comp) {
|
|
|
|
m->ext = strdup(ext + 4);
|
|
|
|
if (!m->ext) {
|
|
|
|
free((void *) m->name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-31 20:01:04 +08:00
|
|
|
void dso__set_module_info(struct dso *dso, struct kmod_path *m,
|
|
|
|
struct machine *machine)
|
|
|
|
{
|
|
|
|
if (machine__is_host(machine))
|
|
|
|
dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE;
|
|
|
|
else
|
|
|
|
dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE;
|
|
|
|
|
|
|
|
/* _KMODULE_COMP should be next to _KMODULE */
|
|
|
|
if (m->kmod && m->comp)
|
|
|
|
dso->symtab_type++;
|
|
|
|
|
|
|
|
dso__set_short_name(dso, strdup(m->name), true);
|
|
|
|
}
|
|
|
|
|
2014-04-30 21:00:59 +08:00
|
|
|
/*
|
2014-04-30 21:25:10 +08:00
|
|
|
* Global list of open DSOs and the counter.
|
2014-04-30 21:00:59 +08:00
|
|
|
*/
|
|
|
|
static LIST_HEAD(dso__data_open);
|
2014-04-30 21:25:10 +08:00
|
|
|
static long dso__data_open_cnt;
|
2015-05-18 08:30:42 +08:00
|
|
|
static pthread_mutex_t dso__data_open_lock = PTHREAD_MUTEX_INITIALIZER;
|
2014-04-30 21:00:59 +08:00
|
|
|
|
|
|
|
static void dso__list_add(struct dso *dso)
|
|
|
|
{
|
|
|
|
list_add_tail(&dso->data.open_entry, &dso__data_open);
|
2014-04-30 21:25:10 +08:00
|
|
|
dso__data_open_cnt++;
|
2014-04-30 21:00:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dso__list_del(struct dso *dso)
|
|
|
|
{
|
|
|
|
list_del(&dso->data.open_entry);
|
2014-04-30 21:25:10 +08:00
|
|
|
WARN_ONCE(dso__data_open_cnt <= 0,
|
|
|
|
"DSO data fd counter out of bounds.");
|
|
|
|
dso__data_open_cnt--;
|
2014-04-30 21:00:59 +08:00
|
|
|
}
|
|
|
|
|
2014-05-08 03:35:02 +08:00
|
|
|
static void close_first_dso(void);
|
|
|
|
|
|
|
|
static int do_open(char *name)
|
|
|
|
{
|
|
|
|
int fd;
|
2014-08-14 10:22:36 +08:00
|
|
|
char sbuf[STRERR_BUFSIZE];
|
2014-05-08 03:35:02 +08:00
|
|
|
|
|
|
|
do {
|
perf symbols: Using O_CLOEXEC in do_open
I've meet a strange behavior with these commands on my gentoo box:
1: perf kmem record
2: CTRL-C to stop 1
3: perf report
4: "Enter", "Enter", "Run scripts for all samples",
"event_analyzing_sample".
Then 'perf report' says:
"
No kallsyms or vmlinux with build-id xxxx was found
/lib/modules/4.10.0+/build/vmlinux with build id xxxx not found,
continuing without symbols
".
It is strange because I am sure /lib/modules/4.10.0+/build/vmlinux is
right for perf.data.
After digging, I found out the reason is that "perf report" generates
many open fds, then "script_browse" uses popen to run "perf script"
which run out of open files.
The gentoo box has a small default value for "max open files", 1024.
Yes, "ulimit -n " with a bigger number could fix it, but I think that
using O_CLOEXEC in do_open is a better way.
Signed-off-by: Wang YanQing <udknight@gmail.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20180115050448.GA20759@udknight
[ Make sure O_CLOEXEC is available in old systems by adding a patch
just before this one, to keep this bisectable in such systems ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-01-15 13:04:48 +08:00
|
|
|
fd = open(name, O_RDONLY|O_CLOEXEC);
|
2014-05-08 03:35:02 +08:00
|
|
|
if (fd >= 0)
|
|
|
|
return fd;
|
|
|
|
|
2015-01-30 10:33:29 +08:00
|
|
|
pr_debug("dso open failed: %s\n",
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 22:56:20 +08:00
|
|
|
str_error_r(errno, sbuf, sizeof(sbuf)));
|
2014-05-08 03:35:02 +08:00
|
|
|
if (!dso__data_open_cnt || errno != EMFILE)
|
|
|
|
break;
|
|
|
|
|
|
|
|
close_first_dso();
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-04-30 21:00:59 +08:00
|
|
|
static int __open_dso(struct dso *dso, struct machine *machine)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2017-06-08 15:31:06 +08:00
|
|
|
int fd = -EINVAL;
|
2013-12-17 04:03:18 +08:00
|
|
|
char *root_dir = (char *)"";
|
|
|
|
char *name = malloc(PATH_MAX);
|
2018-08-17 17:48:03 +08:00
|
|
|
bool decomp = false;
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
if (!name)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (machine)
|
|
|
|
root_dir = machine->root_dir;
|
|
|
|
|
2013-12-18 03:14:07 +08:00
|
|
|
if (dso__read_binary_type_filename(dso, dso->binary_type,
|
2017-06-08 15:31:06 +08:00
|
|
|
root_dir, name, PATH_MAX))
|
|
|
|
goto out;
|
2012-10-28 05:18:32 +08:00
|
|
|
|
2017-06-08 15:31:06 +08:00
|
|
|
if (!is_regular_file(name))
|
|
|
|
goto out;
|
2016-09-21 00:12:45 +08:00
|
|
|
|
2017-06-08 15:31:05 +08:00
|
|
|
if (dso__needs_decompress(dso)) {
|
|
|
|
char newpath[KMOD_DECOMP_LEN];
|
|
|
|
size_t len = sizeof(newpath);
|
|
|
|
|
|
|
|
if (dso__decompress_kmodule_path(dso, name, newpath, len) < 0) {
|
2017-06-08 15:31:06 +08:00
|
|
|
fd = -dso->load_errno;
|
|
|
|
goto out;
|
2017-06-08 15:31:05 +08:00
|
|
|
}
|
|
|
|
|
2018-08-17 17:48:03 +08:00
|
|
|
decomp = true;
|
2017-06-08 15:31:05 +08:00
|
|
|
strcpy(name, newpath);
|
|
|
|
}
|
|
|
|
|
2014-05-08 03:35:02 +08:00
|
|
|
fd = do_open(name);
|
2017-06-08 15:31:05 +08:00
|
|
|
|
2018-08-17 17:48:03 +08:00
|
|
|
if (decomp)
|
2017-06-08 15:31:05 +08:00
|
|
|
unlink(name);
|
|
|
|
|
2017-06-08 15:31:06 +08:00
|
|
|
out:
|
2012-10-28 05:18:32 +08:00
|
|
|
free(name);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
static void check_data_close(void);
|
|
|
|
|
2014-05-08 03:09:59 +08:00
|
|
|
/**
|
|
|
|
* dso_close - Open DSO data file
|
|
|
|
* @dso: dso object
|
|
|
|
*
|
|
|
|
* Open @dso's data file descriptor and updates
|
|
|
|
* list/count of open DSO objects.
|
|
|
|
*/
|
2014-04-30 21:00:59 +08:00
|
|
|
static int open_dso(struct dso *dso, struct machine *machine)
|
|
|
|
{
|
2017-07-06 09:48:11 +08:00
|
|
|
int fd;
|
|
|
|
struct nscookie nsc;
|
|
|
|
|
|
|
|
if (dso->binary_type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
|
|
|
|
nsinfo__mountns_enter(dso->nsinfo, &nsc);
|
|
|
|
fd = __open_dso(dso, machine);
|
|
|
|
if (dso->binary_type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
|
|
|
|
nsinfo__mountns_exit(&nsc);
|
2014-04-30 21:00:59 +08:00
|
|
|
|
2014-07-17 16:43:09 +08:00
|
|
|
if (fd >= 0) {
|
2014-04-30 21:00:59 +08:00
|
|
|
dso__list_add(dso);
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
/*
|
|
|
|
* Check if we crossed the allowed number
|
|
|
|
* of opened DSOs and close one if needed.
|
|
|
|
*/
|
|
|
|
check_data_close();
|
|
|
|
}
|
2014-04-30 21:00:59 +08:00
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void close_data_fd(struct dso *dso)
|
2014-04-28 22:43:43 +08:00
|
|
|
{
|
|
|
|
if (dso->data.fd >= 0) {
|
|
|
|
close(dso->data.fd);
|
|
|
|
dso->data.fd = -1;
|
2014-05-08 00:51:41 +08:00
|
|
|
dso->data.file_size = 0;
|
2014-04-30 21:00:59 +08:00
|
|
|
dso__list_del(dso);
|
2014-04-28 22:43:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-08 03:09:59 +08:00
|
|
|
/**
|
|
|
|
* dso_close - Close DSO data file
|
|
|
|
* @dso: dso object
|
|
|
|
*
|
|
|
|
* Close @dso's data file descriptor and updates
|
|
|
|
* list/count of open DSO objects.
|
|
|
|
*/
|
2014-04-30 21:00:59 +08:00
|
|
|
static void close_dso(struct dso *dso)
|
|
|
|
{
|
|
|
|
close_data_fd(dso);
|
|
|
|
}
|
|
|
|
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
static void close_first_dso(void)
|
|
|
|
{
|
|
|
|
struct dso *dso;
|
|
|
|
|
|
|
|
dso = list_first_entry(&dso__data_open, struct dso, data.open_entry);
|
|
|
|
close_dso(dso);
|
|
|
|
}
|
|
|
|
|
|
|
|
static rlim_t get_fd_limit(void)
|
|
|
|
{
|
|
|
|
struct rlimit l;
|
|
|
|
rlim_t limit = 0;
|
|
|
|
|
|
|
|
/* Allow half of the current open fd limit. */
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &l) == 0) {
|
|
|
|
if (l.rlim_cur == RLIM_INFINITY)
|
|
|
|
limit = l.rlim_cur;
|
|
|
|
else
|
|
|
|
limit = l.rlim_cur / 2;
|
|
|
|
} else {
|
|
|
|
pr_err("failed to get fd limit\n");
|
|
|
|
limit = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return limit;
|
|
|
|
}
|
|
|
|
|
2016-06-28 19:29:02 +08:00
|
|
|
static rlim_t fd_limit;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Used only by tests/dso-data.c to reset the environment
|
|
|
|
* for tests. I dont expect we should change this during
|
|
|
|
* standard runtime.
|
|
|
|
*/
|
|
|
|
void reset_fd_limit(void)
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
{
|
2016-06-28 19:29:02 +08:00
|
|
|
fd_limit = 0;
|
|
|
|
}
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
|
2016-06-28 19:29:02 +08:00
|
|
|
static bool may_cache_fd(void)
|
|
|
|
{
|
|
|
|
if (!fd_limit)
|
|
|
|
fd_limit = get_fd_limit();
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
|
2016-06-28 19:29:02 +08:00
|
|
|
if (fd_limit == RLIM_INFINITY)
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
return true;
|
|
|
|
|
2016-06-28 19:29:02 +08:00
|
|
|
return fd_limit > (rlim_t) dso__data_open_cnt;
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
}
|
|
|
|
|
2014-05-08 03:09:59 +08:00
|
|
|
/*
|
|
|
|
* Check and close LRU dso if we crossed allowed limit
|
|
|
|
* for opened dso file descriptors. The limit is half
|
|
|
|
* of the RLIMIT_NOFILE files opened.
|
|
|
|
*/
|
perf tools: Cache dso data file descriptor
Caching dso data file descriptors to avoid expensive re-opens
especially during DWARF unwind.
We keep dsos data file descriptors open until their count reaches
the half of the current fd open limit (RLIMIT_NOFILE). In this case
we close file descriptor of the first opened dso object.
We've got overall speedup (~27% for my workload) of report:
'perf report --stdio -i perf-test.data' (3 runs)
(perf-test.data size was around 12GB)
current code:
545,640,944,228 cycles ( +- 0.53% )
785,255,798,320 instructions ( +- 0.03% )
366.340910010 seconds time elapsed ( +- 3.65% )
after change:
435,895,036,114 cycles ( +- 0.26% )
636,790,271,176 instructions ( +- 0.04% )
266.481463387 seconds time elapsed ( +- 0.13% )
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jean Pihet <jean.pihet@linaro.org>
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/1401892622-30848-7-git-send-email-jolsa@kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
2014-04-30 21:47:27 +08:00
|
|
|
static void check_data_close(void)
|
|
|
|
{
|
|
|
|
bool cache_fd = may_cache_fd();
|
|
|
|
|
|
|
|
if (!cache_fd)
|
|
|
|
close_first_dso();
|
|
|
|
}
|
|
|
|
|
2014-05-08 03:09:59 +08:00
|
|
|
/**
|
|
|
|
* dso__data_close - Close DSO data file
|
|
|
|
* @dso: dso object
|
|
|
|
*
|
|
|
|
* External interface to close @dso's data file descriptor.
|
|
|
|
*/
|
2014-04-30 21:00:59 +08:00
|
|
|
void dso__data_close(struct dso *dso)
|
|
|
|
{
|
2015-05-18 08:30:42 +08:00
|
|
|
pthread_mutex_lock(&dso__data_open_lock);
|
2014-04-30 21:00:59 +08:00
|
|
|
close_dso(dso);
|
2015-05-18 08:30:42 +08:00
|
|
|
pthread_mutex_unlock(&dso__data_open_lock);
|
2014-04-30 21:00:59 +08:00
|
|
|
}
|
|
|
|
|
2015-05-21 00:03:39 +08:00
|
|
|
static void try_to_open_dso(struct dso *dso, struct machine *machine)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2013-12-17 03:57:43 +08:00
|
|
|
enum dso_binary_type binary_type_data[] = {
|
2012-10-28 05:18:32 +08:00
|
|
|
DSO_BINARY_TYPE__BUILD_ID_CACHE,
|
|
|
|
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
|
|
|
|
DSO_BINARY_TYPE__NOT_FOUND,
|
|
|
|
};
|
|
|
|
int i = 0;
|
|
|
|
|
2014-04-28 22:43:43 +08:00
|
|
|
if (dso->data.fd >= 0)
|
2015-05-21 00:03:39 +08:00
|
|
|
return;
|
2014-04-28 22:43:43 +08:00
|
|
|
|
|
|
|
if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) {
|
|
|
|
dso->data.fd = open_dso(dso, machine);
|
2014-07-22 21:17:18 +08:00
|
|
|
goto out;
|
2014-04-28 22:43:43 +08:00
|
|
|
}
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
do {
|
2013-12-18 03:14:07 +08:00
|
|
|
dso->binary_type = binary_type_data[i++];
|
2012-10-28 05:18:32 +08:00
|
|
|
|
2014-07-22 21:17:18 +08:00
|
|
|
dso->data.fd = open_dso(dso, machine);
|
|
|
|
if (dso->data.fd >= 0)
|
|
|
|
goto out;
|
2012-10-28 05:18:32 +08:00
|
|
|
|
2013-12-18 03:14:07 +08:00
|
|
|
} while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND);
|
2014-07-22 21:17:18 +08:00
|
|
|
out:
|
|
|
|
if (dso->data.fd >= 0)
|
|
|
|
dso->data.status = DSO_DATA_STATUS_OK;
|
|
|
|
else
|
|
|
|
dso->data.status = DSO_DATA_STATUS_ERROR;
|
2015-05-21 00:03:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-05-21 00:03:41 +08:00
|
|
|
* dso__data_get_fd - Get dso's data file descriptor
|
2015-05-21 00:03:39 +08:00
|
|
|
* @dso: dso object
|
|
|
|
* @machine: machine object
|
|
|
|
*
|
|
|
|
* External interface to find dso's file, open it and
|
2015-05-21 00:03:41 +08:00
|
|
|
* returns file descriptor. It should be paired with
|
|
|
|
* dso__data_put_fd() if it returns non-negative value.
|
2015-05-21 00:03:39 +08:00
|
|
|
*/
|
2015-05-21 00:03:41 +08:00
|
|
|
int dso__data_get_fd(struct dso *dso, struct machine *machine)
|
2015-05-21 00:03:39 +08:00
|
|
|
{
|
|
|
|
if (dso->data.status == DSO_DATA_STATUS_ERROR)
|
|
|
|
return -1;
|
2012-10-28 05:18:32 +08:00
|
|
|
|
2015-05-21 00:03:41 +08:00
|
|
|
if (pthread_mutex_lock(&dso__data_open_lock) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2015-05-21 00:03:39 +08:00
|
|
|
try_to_open_dso(dso, machine);
|
2015-05-21 00:03:41 +08:00
|
|
|
|
|
|
|
if (dso->data.fd < 0)
|
|
|
|
pthread_mutex_unlock(&dso__data_open_lock);
|
2015-05-21 00:03:39 +08:00
|
|
|
|
2014-07-22 21:17:18 +08:00
|
|
|
return dso->data.fd;
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
2015-05-21 00:03:41 +08:00
|
|
|
void dso__data_put_fd(struct dso *dso __maybe_unused)
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&dso__data_open_lock);
|
|
|
|
}
|
|
|
|
|
2014-07-22 21:17:19 +08:00
|
|
|
bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by)
|
|
|
|
{
|
|
|
|
u32 flag = 1 << by;
|
|
|
|
|
|
|
|
if (dso->data.status_seen & flag)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
dso->data.status_seen |= flag;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
static void
|
2015-05-18 08:30:41 +08:00
|
|
|
dso_cache__free(struct dso *dso)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2015-05-18 08:30:41 +08:00
|
|
|
struct rb_root *root = &dso->data.cache;
|
2012-10-28 05:18:32 +08:00
|
|
|
struct rb_node *next = rb_first(root);
|
|
|
|
|
2015-05-18 08:30:41 +08:00
|
|
|
pthread_mutex_lock(&dso->lock);
|
2012-10-28 05:18:32 +08:00
|
|
|
while (next) {
|
|
|
|
struct dso_cache *cache;
|
|
|
|
|
|
|
|
cache = rb_entry(next, struct dso_cache, rb_node);
|
|
|
|
next = rb_next(&cache->rb_node);
|
|
|
|
rb_erase(&cache->rb_node, root);
|
|
|
|
free(cache);
|
|
|
|
}
|
2015-05-18 08:30:41 +08:00
|
|
|
pthread_mutex_unlock(&dso->lock);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
2015-05-18 08:30:41 +08:00
|
|
|
static struct dso_cache *dso_cache__find(struct dso *dso, u64 offset)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2015-05-18 08:30:41 +08:00
|
|
|
const struct rb_root *root = &dso->data.cache;
|
2013-12-11 02:46:29 +08:00
|
|
|
struct rb_node * const *p = &root->rb_node;
|
|
|
|
const struct rb_node *parent = NULL;
|
2012-10-28 05:18:32 +08:00
|
|
|
struct dso_cache *cache;
|
|
|
|
|
|
|
|
while (*p != NULL) {
|
|
|
|
u64 end;
|
|
|
|
|
|
|
|
parent = *p;
|
|
|
|
cache = rb_entry(parent, struct dso_cache, rb_node);
|
|
|
|
end = cache->offset + DSO__DATA_CACHE_SIZE;
|
|
|
|
|
|
|
|
if (offset < cache->offset)
|
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else if (offset >= end)
|
|
|
|
p = &(*p)->rb_right;
|
|
|
|
else
|
|
|
|
return cache;
|
|
|
|
}
|
2015-05-18 08:30:41 +08:00
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-05-18 08:30:41 +08:00
|
|
|
static struct dso_cache *
|
|
|
|
dso_cache__insert(struct dso *dso, struct dso_cache *new)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2015-05-18 08:30:41 +08:00
|
|
|
struct rb_root *root = &dso->data.cache;
|
2012-10-28 05:18:32 +08:00
|
|
|
struct rb_node **p = &root->rb_node;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct dso_cache *cache;
|
|
|
|
u64 offset = new->offset;
|
|
|
|
|
2015-05-18 08:30:41 +08:00
|
|
|
pthread_mutex_lock(&dso->lock);
|
2012-10-28 05:18:32 +08:00
|
|
|
while (*p != NULL) {
|
|
|
|
u64 end;
|
|
|
|
|
|
|
|
parent = *p;
|
|
|
|
cache = rb_entry(parent, struct dso_cache, rb_node);
|
|
|
|
end = cache->offset + DSO__DATA_CACHE_SIZE;
|
|
|
|
|
|
|
|
if (offset < cache->offset)
|
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else if (offset >= end)
|
|
|
|
p = &(*p)->rb_right;
|
2015-05-18 08:30:41 +08:00
|
|
|
else
|
|
|
|
goto out;
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
rb_link_node(&new->rb_node, parent, p);
|
|
|
|
rb_insert_color(&new->rb_node, root);
|
2015-05-18 08:30:41 +08:00
|
|
|
|
|
|
|
cache = NULL;
|
|
|
|
out:
|
|
|
|
pthread_mutex_unlock(&dso->lock);
|
|
|
|
return cache;
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
dso_cache__memcpy(struct dso_cache *cache, u64 offset,
|
|
|
|
u8 *data, u64 size)
|
|
|
|
{
|
|
|
|
u64 cache_offset = offset - cache->offset;
|
|
|
|
u64 cache_size = min(cache->size - cache_offset, size);
|
|
|
|
|
|
|
|
memcpy(data, cache->data + cache_offset, cache_size);
|
|
|
|
return cache_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
2015-05-18 08:30:42 +08:00
|
|
|
dso_cache__read(struct dso *dso, struct machine *machine,
|
|
|
|
u64 offset, u8 *data, ssize_t size)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
|
|
|
struct dso_cache *cache;
|
2015-05-18 08:30:41 +08:00
|
|
|
struct dso_cache *old;
|
2012-10-28 05:18:32 +08:00
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
do {
|
|
|
|
u64 cache_offset;
|
|
|
|
|
|
|
|
cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
|
|
|
|
if (!cache)
|
2015-05-18 08:30:42 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&dso__data_open_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* dso->data.fd might be closed if other thread opened another
|
|
|
|
* file (dso) due to open file limit (RLIMIT_NOFILE).
|
|
|
|
*/
|
2015-05-21 00:03:39 +08:00
|
|
|
try_to_open_dso(dso, machine);
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
if (dso->data.fd < 0) {
|
2015-05-21 00:03:39 +08:00
|
|
|
ret = -errno;
|
|
|
|
dso->data.status = DSO_DATA_STATUS_ERROR;
|
|
|
|
break;
|
2015-05-18 08:30:42 +08:00
|
|
|
}
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
cache_offset = offset & DSO__DATA_CACHE_MASK;
|
|
|
|
|
2015-01-30 04:02:01 +08:00
|
|
|
ret = pread(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE, cache_offset);
|
2012-10-28 05:18:32 +08:00
|
|
|
if (ret <= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
cache->offset = cache_offset;
|
|
|
|
cache->size = ret;
|
2015-05-18 08:30:42 +08:00
|
|
|
} while (0);
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&dso__data_open_lock);
|
|
|
|
|
|
|
|
if (ret > 0) {
|
2015-05-18 08:30:41 +08:00
|
|
|
old = dso_cache__insert(dso, cache);
|
|
|
|
if (old) {
|
|
|
|
/* we lose the race */
|
|
|
|
free(cache);
|
|
|
|
cache = old;
|
|
|
|
}
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
ret = dso_cache__memcpy(cache, offset, data, size);
|
2015-05-18 08:30:42 +08:00
|
|
|
}
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
if (ret <= 0)
|
|
|
|
free(cache);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
|
|
|
|
u64 offset, u8 *data, ssize_t size)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
|
|
|
struct dso_cache *cache;
|
|
|
|
|
2015-05-18 08:30:41 +08:00
|
|
|
cache = dso_cache__find(dso, offset);
|
2012-10-28 05:18:32 +08:00
|
|
|
if (cache)
|
|
|
|
return dso_cache__memcpy(cache, offset, data, size);
|
|
|
|
else
|
2015-05-18 08:30:42 +08:00
|
|
|
return dso_cache__read(dso, machine, offset, data, size);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
2014-05-08 03:09:59 +08:00
|
|
|
/*
|
|
|
|
* Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks
|
|
|
|
* in the rb_tree. Any read to already cached data is served
|
|
|
|
* by cached data.
|
|
|
|
*/
|
2015-05-18 08:30:42 +08:00
|
|
|
static ssize_t cached_read(struct dso *dso, struct machine *machine,
|
|
|
|
u64 offset, u8 *data, ssize_t size)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
|
|
|
ssize_t r = 0;
|
|
|
|
u8 *p = data;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ssize_t ret;
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
ret = dso_cache_read(dso, machine, offset, p, size);
|
2012-10-28 05:18:32 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Reached EOF, return what we have. */
|
|
|
|
if (!ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
BUG_ON(ret > size);
|
|
|
|
|
|
|
|
r += ret;
|
|
|
|
p += ret;
|
|
|
|
offset += ret;
|
|
|
|
size -= ret;
|
|
|
|
|
|
|
|
} while (size);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
static int data_file_size(struct dso *dso, struct machine *machine)
|
2014-05-08 00:51:41 +08:00
|
|
|
{
|
2015-05-18 08:30:42 +08:00
|
|
|
int ret = 0;
|
2014-05-08 00:51:41 +08:00
|
|
|
struct stat st;
|
2014-08-14 10:22:36 +08:00
|
|
|
char sbuf[STRERR_BUFSIZE];
|
2014-05-08 00:51:41 +08:00
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
if (dso->data.file_size)
|
|
|
|
return 0;
|
|
|
|
|
2015-05-21 00:03:39 +08:00
|
|
|
if (dso->data.status == DSO_DATA_STATUS_ERROR)
|
|
|
|
return -1;
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
pthread_mutex_lock(&dso__data_open_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* dso->data.fd might be closed if other thread opened another
|
|
|
|
* file (dso) due to open file limit (RLIMIT_NOFILE).
|
|
|
|
*/
|
2015-05-21 00:03:39 +08:00
|
|
|
try_to_open_dso(dso, machine);
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
if (dso->data.fd < 0) {
|
2015-05-21 00:03:39 +08:00
|
|
|
ret = -errno;
|
|
|
|
dso->data.status = DSO_DATA_STATUS_ERROR;
|
|
|
|
goto out;
|
2014-05-08 00:51:41 +08:00
|
|
|
}
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
if (fstat(dso->data.fd, &st) < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
pr_err("dso cache fstat failed: %s\n",
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 22:56:20 +08:00
|
|
|
str_error_r(errno, sbuf, sizeof(sbuf)));
|
2015-05-18 08:30:42 +08:00
|
|
|
dso->data.status = DSO_DATA_STATUS_ERROR;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
dso->data.file_size = st.st_size;
|
|
|
|
|
|
|
|
out:
|
|
|
|
pthread_mutex_unlock(&dso__data_open_lock);
|
|
|
|
return ret;
|
2014-05-08 00:51:41 +08:00
|
|
|
}
|
|
|
|
|
2014-07-22 21:17:35 +08:00
|
|
|
/**
|
|
|
|
* dso__data_size - Return dso data size
|
|
|
|
* @dso: dso object
|
|
|
|
* @machine: machine object
|
|
|
|
*
|
|
|
|
* Return: dso data size
|
|
|
|
*/
|
|
|
|
off_t dso__data_size(struct dso *dso, struct machine *machine)
|
|
|
|
{
|
2015-05-18 08:30:42 +08:00
|
|
|
if (data_file_size(dso, machine))
|
2014-07-22 21:17:35 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* For now just estimate dso data size is close to file size */
|
|
|
|
return dso->data.file_size;
|
|
|
|
}
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
static ssize_t data_read_offset(struct dso *dso, struct machine *machine,
|
|
|
|
u64 offset, u8 *data, ssize_t size)
|
2014-05-08 00:51:41 +08:00
|
|
|
{
|
2015-05-18 08:30:42 +08:00
|
|
|
if (data_file_size(dso, machine))
|
2014-05-08 00:51:41 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Check the offset sanity. */
|
|
|
|
if (offset > dso->data.file_size)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (offset + size < offset)
|
|
|
|
return -1;
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
return cached_read(dso, machine, offset, data, size);
|
2014-05-08 00:51:41 +08:00
|
|
|
}
|
|
|
|
|
2014-05-08 03:09:59 +08:00
|
|
|
/**
|
|
|
|
* dso__data_read_offset - Read data from dso file offset
|
|
|
|
* @dso: dso object
|
|
|
|
* @machine: machine object
|
|
|
|
* @offset: file offset
|
|
|
|
* @data: buffer to store data
|
|
|
|
* @size: size of the @data buffer
|
|
|
|
*
|
|
|
|
* External interface to read data from dso file offset. Open
|
|
|
|
* dso data file and use cached_read to get the data.
|
|
|
|
*/
|
2014-05-08 00:51:41 +08:00
|
|
|
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
|
|
|
|
u64 offset, u8 *data, ssize_t size)
|
|
|
|
{
|
2015-05-18 08:30:42 +08:00
|
|
|
if (dso->data.status == DSO_DATA_STATUS_ERROR)
|
2014-05-08 00:51:41 +08:00
|
|
|
return -1;
|
|
|
|
|
2015-05-18 08:30:42 +08:00
|
|
|
return data_read_offset(dso, machine, offset, data, size);
|
2014-05-08 00:51:41 +08:00
|
|
|
}
|
|
|
|
|
2014-05-08 03:09:59 +08:00
|
|
|
/**
|
|
|
|
* dso__data_read_addr - Read data from dso address
|
|
|
|
* @dso: dso object
|
|
|
|
* @machine: machine object
|
|
|
|
* @add: virtual memory address
|
|
|
|
* @data: buffer to store data
|
|
|
|
* @size: size of the @data buffer
|
|
|
|
*
|
|
|
|
* External interface to read data from dso address.
|
|
|
|
*/
|
2012-10-28 05:18:32 +08:00
|
|
|
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
|
|
|
|
struct machine *machine, u64 addr,
|
|
|
|
u8 *data, ssize_t size)
|
|
|
|
{
|
|
|
|
u64 offset = map->map_ip(map, addr);
|
|
|
|
return dso__data_read_offset(dso, machine, offset, data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct map *dso__new_map(const char *name)
|
|
|
|
{
|
|
|
|
struct map *map = NULL;
|
|
|
|
struct dso *dso = dso__new(name);
|
|
|
|
|
|
|
|
if (dso)
|
2018-04-27 03:52:34 +08:00
|
|
|
map = map__new2(0, dso);
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2015-05-28 23:40:55 +08:00
|
|
|
struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
|
|
|
|
const char *short_name, int dso_type)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The kernel dso could be created by build_id processing.
|
|
|
|
*/
|
2015-05-29 22:31:12 +08:00
|
|
|
struct dso *dso = machine__findnew_dso(machine, name);
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to run this in all cases, since during the build_id
|
|
|
|
* processing we had no idea this was the kernel dso.
|
|
|
|
*/
|
|
|
|
if (dso != NULL) {
|
2013-12-10 22:11:46 +08:00
|
|
|
dso__set_short_name(dso, short_name, false);
|
2012-10-28 05:18:32 +08:00
|
|
|
dso->kernel = dso_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dso;
|
|
|
|
}
|
|
|
|
|
2014-10-01 01:36:15 +08:00
|
|
|
/*
|
|
|
|
* Find a matching entry and/or link current entry to RB tree.
|
|
|
|
* Either one of the dso or name parameter must be non-NULL or the
|
|
|
|
* function will not work.
|
|
|
|
*/
|
2015-06-02 02:40:01 +08:00
|
|
|
static struct dso *__dso__findlink_by_longname(struct rb_root *root,
|
|
|
|
struct dso *dso, const char *name)
|
2014-10-01 01:36:15 +08:00
|
|
|
{
|
|
|
|
struct rb_node **p = &root->rb_node;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
name = dso->long_name;
|
|
|
|
/*
|
|
|
|
* Find node with the matching name
|
|
|
|
*/
|
|
|
|
while (*p) {
|
|
|
|
struct dso *this = rb_entry(*p, struct dso, rb_node);
|
|
|
|
int rc = strcmp(name, this->long_name);
|
|
|
|
|
|
|
|
parent = *p;
|
|
|
|
if (rc == 0) {
|
|
|
|
/*
|
|
|
|
* In case the new DSO is a duplicate of an existing
|
2017-02-28 06:28:52 +08:00
|
|
|
* one, print a one-time warning & put the new entry
|
2014-10-01 01:36:15 +08:00
|
|
|
* at the end of the list of duplicates.
|
|
|
|
*/
|
|
|
|
if (!dso || (dso == this))
|
|
|
|
return this; /* Find matching dso */
|
|
|
|
/*
|
|
|
|
* The core kernel DSOs may have duplicated long name.
|
|
|
|
* In this case, the short name should be different.
|
|
|
|
* Comparing the short names to differentiate the DSOs.
|
|
|
|
*/
|
|
|
|
rc = strcmp(dso->short_name, this->short_name);
|
|
|
|
if (rc == 0) {
|
|
|
|
pr_err("Duplicated dso name: %s\n", name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rc < 0)
|
|
|
|
p = &parent->rb_left;
|
|
|
|
else
|
|
|
|
p = &parent->rb_right;
|
|
|
|
}
|
|
|
|
if (dso) {
|
|
|
|
/* Add new node and rebalance tree */
|
|
|
|
rb_link_node(&dso->rb_node, parent, p);
|
|
|
|
rb_insert_color(&dso->rb_node, root);
|
2015-11-13 17:48:30 +08:00
|
|
|
dso->root = root;
|
2014-10-01 01:36:15 +08:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-06-02 02:40:01 +08:00
|
|
|
static inline struct dso *__dso__find_by_longname(struct rb_root *root,
|
|
|
|
const char *name)
|
2014-10-01 01:36:15 +08:00
|
|
|
{
|
2015-06-02 02:40:01 +08:00
|
|
|
return __dso__findlink_by_longname(root, NULL, name);
|
2014-10-01 01:36:15 +08:00
|
|
|
}
|
|
|
|
|
2013-12-11 02:19:23 +08:00
|
|
|
void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2015-11-13 17:48:30 +08:00
|
|
|
struct rb_root *root = dso->root;
|
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
if (name == NULL)
|
|
|
|
return;
|
2013-12-11 02:08:44 +08:00
|
|
|
|
|
|
|
if (dso->long_name_allocated)
|
2013-12-11 02:19:23 +08:00
|
|
|
free((char *)dso->long_name);
|
2013-12-11 02:08:44 +08:00
|
|
|
|
2015-11-13 17:48:30 +08:00
|
|
|
if (root) {
|
|
|
|
rb_erase(&dso->rb_node, root);
|
|
|
|
/*
|
|
|
|
* __dso__findlink_by_longname() isn't guaranteed to add it
|
|
|
|
* back, so a clean removal is required here.
|
|
|
|
*/
|
|
|
|
RB_CLEAR_NODE(&dso->rb_node);
|
|
|
|
dso->root = NULL;
|
|
|
|
}
|
|
|
|
|
2013-12-11 02:08:44 +08:00
|
|
|
dso->long_name = name;
|
|
|
|
dso->long_name_len = strlen(name);
|
|
|
|
dso->long_name_allocated = name_allocated;
|
2015-11-13 17:48:30 +08:00
|
|
|
|
|
|
|
if (root)
|
|
|
|
__dso__findlink_by_longname(root, dso, NULL);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
2013-12-10 22:11:46 +08:00
|
|
|
void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
|
|
|
if (name == NULL)
|
|
|
|
return;
|
2013-12-10 22:11:46 +08:00
|
|
|
|
|
|
|
if (dso->short_name_allocated)
|
|
|
|
free((char *)dso->short_name);
|
|
|
|
|
|
|
|
dso->short_name = name;
|
|
|
|
dso->short_name_len = strlen(name);
|
|
|
|
dso->short_name_allocated = name_allocated;
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dso__set_basename(struct dso *dso)
|
|
|
|
{
|
2013-12-06 02:26:42 +08:00
|
|
|
/*
|
|
|
|
* basename() may modify path buffer, so we must pass
|
|
|
|
* a copy.
|
|
|
|
*/
|
|
|
|
char *base, *lname = strdup(dso->long_name);
|
|
|
|
|
|
|
|
if (!lname)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* basename() may return a pointer to internal
|
|
|
|
* storage which is reused in subsequent calls
|
|
|
|
* so copy the result.
|
|
|
|
*/
|
|
|
|
base = strdup(basename(lname));
|
|
|
|
|
|
|
|
free(lname);
|
|
|
|
|
|
|
|
if (!base)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dso__set_short_name(dso, base, true);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int dso__name_len(const struct dso *dso)
|
|
|
|
{
|
|
|
|
if (!dso)
|
|
|
|
return strlen("[unknown]");
|
2017-02-17 16:17:38 +08:00
|
|
|
if (verbose > 0)
|
2012-10-28 05:18:32 +08:00
|
|
|
return dso->long_name_len;
|
|
|
|
|
|
|
|
return dso->short_name_len;
|
|
|
|
}
|
|
|
|
|
2018-04-27 03:52:34 +08:00
|
|
|
bool dso__loaded(const struct dso *dso)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2018-04-27 03:52:34 +08:00
|
|
|
return dso->loaded;
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-27 03:52:34 +08:00
|
|
|
bool dso__sorted_by_name(const struct dso *dso)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2018-04-27 03:52:34 +08:00
|
|
|
return dso->sorted_by_name;
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-27 03:52:34 +08:00
|
|
|
void dso__set_sorted_by_name(struct dso *dso)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2018-04-27 03:52:34 +08:00
|
|
|
dso->sorted_by_name = true;
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct dso *dso__new(const char *name)
|
|
|
|
{
|
|
|
|
struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1);
|
|
|
|
|
|
|
|
if (dso != NULL) {
|
|
|
|
strcpy(dso->name, name);
|
2013-12-11 02:08:44 +08:00
|
|
|
dso__set_long_name(dso, dso->name, false);
|
2013-12-10 22:11:46 +08:00
|
|
|
dso__set_short_name(dso, dso->name, false);
|
2018-04-27 03:52:34 +08:00
|
|
|
dso->symbols = dso->symbol_names = RB_ROOT;
|
2014-05-08 00:30:45 +08:00
|
|
|
dso->data.cache = RB_ROOT;
|
2017-10-10 04:32:59 +08:00
|
|
|
dso->inlined_nodes = RB_ROOT;
|
perf report: Cache srclines for callchain nodes
On one hand this ensures that the memory is properly freed when the DSO
gets freed. On the other hand this significantly speeds up the
processing of the callchain nodes when lots of srclines are requested.
For one of my data files e.g.:
Before:
Performance counter stats for 'perf report -s srcline -g srcline --stdio':
52496.495043 task-clock (msec) # 0.999 CPUs utilized
634 context-switches # 0.012 K/sec
2 cpu-migrations # 0.000 K/sec
191,561 page-faults # 0.004 M/sec
165,074,498,235 cycles # 3.144 GHz
334,170,832,408 instructions # 2.02 insn per cycle
90,220,029,745 branches # 1718.591 M/sec
654,525,177 branch-misses # 0.73% of all branches
52.533273822 seconds time elapsedProcessed 236605 events and lost 40 chunks!
After:
Performance counter stats for 'perf report -s srcline -g srcline --stdio':
22606.323706 task-clock (msec) # 1.000 CPUs utilized
31 context-switches # 0.001 K/sec
0 cpu-migrations # 0.000 K/sec
185,471 page-faults # 0.008 M/sec
71,188,113,681 cycles # 3.149 GHz
133,204,943,083 instructions # 1.87 insn per cycle
34,886,384,979 branches # 1543.214 M/sec
278,214,495 branch-misses # 0.80% of all branches
22.609857253 seconds time elapsed
Note that the difference is only this large when `--inline` is not
passed. In such situations, we would use the inliner cache and thus do
not run this code path that often.
I think that this cache should actually be used in other places, too.
When looking at the valgrind leak report for perf report, we see tons of
srclines being leaked, most notably from calls to
hist_entry__get_srcline. The problem is that get_srcline has many
different formatting options (show_sym, show_addr, potentially even
unwind_inlines when calling __get_srcline directly). As such, the
srcline cannot easily be cached for all calls, or we'd have to add
caches for all formatting combinations (6 so far). An alternative would
be to remove the formatting options and handle that on a different level
- i.e. print the sym/addr on demand wherever we actually output
something. And the unwind_inlines could be moved into a separate
function that does not return the srcline.
Signed-off-by: Milian Wolff <milian.wolff@kdab.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20171019113836.5548-4-milian.wolff@kdab.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-10-19 19:38:34 +08:00
|
|
|
dso->srclines = RB_ROOT;
|
2014-04-28 22:43:43 +08:00
|
|
|
dso->data.fd = -1;
|
2014-07-22 21:17:18 +08:00
|
|
|
dso->data.status = DSO_DATA_STATUS_UNKNOWN;
|
2012-10-28 05:18:32 +08:00
|
|
|
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
|
2013-12-18 03:14:07 +08:00
|
|
|
dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
|
2014-07-14 18:02:41 +08:00
|
|
|
dso->is_64_bit = (sizeof(void *) == 8);
|
2012-10-28 05:18:32 +08:00
|
|
|
dso->loaded = 0;
|
2013-08-07 19:38:50 +08:00
|
|
|
dso->rel = 0;
|
2012-10-28 05:18:32 +08:00
|
|
|
dso->sorted_by_name = 0;
|
|
|
|
dso->has_build_id = 0;
|
2013-09-11 13:09:31 +08:00
|
|
|
dso->has_srcline = 1;
|
2013-12-03 15:23:10 +08:00
|
|
|
dso->a2l_fails = 1;
|
2012-10-28 05:18:32 +08:00
|
|
|
dso->kernel = DSO_TYPE_USER;
|
|
|
|
dso->needs_swap = DSO_SWAP__UNSET;
|
2014-10-01 01:36:15 +08:00
|
|
|
RB_CLEAR_NODE(&dso->rb_node);
|
2015-11-13 17:48:30 +08:00
|
|
|
dso->root = NULL;
|
2012-10-28 05:18:32 +08:00
|
|
|
INIT_LIST_HEAD(&dso->node);
|
2014-04-30 21:00:59 +08:00
|
|
|
INIT_LIST_HEAD(&dso->data.open_entry);
|
2015-05-18 08:30:40 +08:00
|
|
|
pthread_mutex_init(&dso->lock, NULL);
|
2017-02-21 23:34:58 +08:00
|
|
|
refcount_set(&dso->refcnt, 1);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return dso;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dso__delete(struct dso *dso)
|
|
|
|
{
|
2014-10-01 01:36:15 +08:00
|
|
|
if (!RB_EMPTY_NODE(&dso->rb_node))
|
|
|
|
pr_err("DSO %s is still in rbtree when being deleted!\n",
|
|
|
|
dso->long_name);
|
2017-10-10 04:32:59 +08:00
|
|
|
|
|
|
|
/* free inlines first, as they reference symbols */
|
|
|
|
inlines__tree_delete(&dso->inlined_nodes);
|
perf report: Cache srclines for callchain nodes
On one hand this ensures that the memory is properly freed when the DSO
gets freed. On the other hand this significantly speeds up the
processing of the callchain nodes when lots of srclines are requested.
For one of my data files e.g.:
Before:
Performance counter stats for 'perf report -s srcline -g srcline --stdio':
52496.495043 task-clock (msec) # 0.999 CPUs utilized
634 context-switches # 0.012 K/sec
2 cpu-migrations # 0.000 K/sec
191,561 page-faults # 0.004 M/sec
165,074,498,235 cycles # 3.144 GHz
334,170,832,408 instructions # 2.02 insn per cycle
90,220,029,745 branches # 1718.591 M/sec
654,525,177 branch-misses # 0.73% of all branches
52.533273822 seconds time elapsedProcessed 236605 events and lost 40 chunks!
After:
Performance counter stats for 'perf report -s srcline -g srcline --stdio':
22606.323706 task-clock (msec) # 1.000 CPUs utilized
31 context-switches # 0.001 K/sec
0 cpu-migrations # 0.000 K/sec
185,471 page-faults # 0.008 M/sec
71,188,113,681 cycles # 3.149 GHz
133,204,943,083 instructions # 1.87 insn per cycle
34,886,384,979 branches # 1543.214 M/sec
278,214,495 branch-misses # 0.80% of all branches
22.609857253 seconds time elapsed
Note that the difference is only this large when `--inline` is not
passed. In such situations, we would use the inliner cache and thus do
not run this code path that often.
I think that this cache should actually be used in other places, too.
When looking at the valgrind leak report for perf report, we see tons of
srclines being leaked, most notably from calls to
hist_entry__get_srcline. The problem is that get_srcline has many
different formatting options (show_sym, show_addr, potentially even
unwind_inlines when calling __get_srcline directly). As such, the
srcline cannot easily be cached for all calls, or we'd have to add
caches for all formatting combinations (6 so far). An alternative would
be to remove the formatting options and handle that on a different level
- i.e. print the sym/addr on demand wherever we actually output
something. And the unwind_inlines could be moved into a separate
function that does not return the srcline.
Signed-off-by: Milian Wolff <milian.wolff@kdab.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20171019113836.5548-4-milian.wolff@kdab.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-10-19 19:38:34 +08:00
|
|
|
srcline__tree_delete(&dso->srclines);
|
2018-04-27 03:52:34 +08:00
|
|
|
symbols__delete(&dso->symbols);
|
2013-12-11 02:26:55 +08:00
|
|
|
|
|
|
|
if (dso->short_name_allocated) {
|
2013-12-27 04:41:15 +08:00
|
|
|
zfree((char **)&dso->short_name);
|
2013-12-11 02:26:55 +08:00
|
|
|
dso->short_name_allocated = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dso->long_name_allocated) {
|
2013-12-27 04:41:15 +08:00
|
|
|
zfree((char **)&dso->long_name);
|
2013-12-11 02:26:55 +08:00
|
|
|
dso->long_name_allocated = false;
|
|
|
|
}
|
|
|
|
|
2014-04-28 22:43:43 +08:00
|
|
|
dso__data_close(dso);
|
2015-04-09 23:53:55 +08:00
|
|
|
auxtrace_cache__free(dso->auxtrace_cache);
|
2015-05-18 08:30:41 +08:00
|
|
|
dso_cache__free(dso);
|
2013-12-03 15:23:07 +08:00
|
|
|
dso__free_a2l(dso);
|
2013-12-27 04:41:15 +08:00
|
|
|
zfree(&dso->symsrc_filename);
|
2017-07-06 09:48:08 +08:00
|
|
|
nsinfo__zput(dso->nsinfo);
|
2015-05-18 08:30:40 +08:00
|
|
|
pthread_mutex_destroy(&dso->lock);
|
2012-10-28 05:18:32 +08:00
|
|
|
free(dso);
|
|
|
|
}
|
|
|
|
|
2015-06-02 22:53:26 +08:00
|
|
|
struct dso *dso__get(struct dso *dso)
|
|
|
|
{
|
|
|
|
if (dso)
|
2017-02-21 23:34:58 +08:00
|
|
|
refcount_inc(&dso->refcnt);
|
2015-06-02 22:53:26 +08:00
|
|
|
return dso;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dso__put(struct dso *dso)
|
|
|
|
{
|
2017-02-21 23:34:58 +08:00
|
|
|
if (dso && refcount_dec_and_test(&dso->refcnt))
|
2015-06-02 22:53:26 +08:00
|
|
|
dso__delete(dso);
|
|
|
|
}
|
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
void dso__set_build_id(struct dso *dso, void *build_id)
|
|
|
|
{
|
|
|
|
memcpy(dso->build_id, build_id, sizeof(dso->build_id));
|
|
|
|
dso->has_build_id = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dso__build_id_equal(const struct dso *dso, u8 *build_id)
|
|
|
|
{
|
|
|
|
return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine)
|
|
|
|
{
|
|
|
|
char path[PATH_MAX];
|
|
|
|
|
|
|
|
if (machine__is_default_guest(machine))
|
|
|
|
return;
|
|
|
|
sprintf(path, "%s/sys/kernel/notes", machine->root_dir);
|
|
|
|
if (sysfs__read_build_id(path, dso->build_id,
|
|
|
|
sizeof(dso->build_id)) == 0)
|
|
|
|
dso->has_build_id = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dso__kernel_module_get_build_id(struct dso *dso,
|
|
|
|
const char *root_dir)
|
|
|
|
{
|
|
|
|
char filename[PATH_MAX];
|
|
|
|
/*
|
|
|
|
* kernel module short names are of the form "[module]" and
|
|
|
|
* we need just "module" here.
|
|
|
|
*/
|
|
|
|
const char *name = dso->short_name + 1;
|
|
|
|
|
|
|
|
snprintf(filename, sizeof(filename),
|
|
|
|
"%s/sys/module/%.*s/notes/.note.gnu.build-id",
|
|
|
|
root_dir, (int)strlen(name) - 1, name);
|
|
|
|
|
|
|
|
if (sysfs__read_build_id(filename, dso->build_id,
|
|
|
|
sizeof(dso->build_id)) == 0)
|
|
|
|
dso->has_build_id = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
|
|
|
|
{
|
|
|
|
bool have_build_id = false;
|
|
|
|
struct dso *pos;
|
2017-07-06 09:48:11 +08:00
|
|
|
struct nscookie nsc;
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
list_for_each_entry(pos, head, node) {
|
2016-05-12 16:43:11 +08:00
|
|
|
if (with_hits && !pos->hit && !dso__is_vdso(pos))
|
2012-10-28 05:18:32 +08:00
|
|
|
continue;
|
|
|
|
if (pos->has_build_id) {
|
|
|
|
have_build_id = true;
|
|
|
|
continue;
|
|
|
|
}
|
2017-07-06 09:48:11 +08:00
|
|
|
nsinfo__mountns_enter(pos->nsinfo, &nsc);
|
2012-10-28 05:18:32 +08:00
|
|
|
if (filename__read_build_id(pos->long_name, pos->build_id,
|
|
|
|
sizeof(pos->build_id)) > 0) {
|
|
|
|
have_build_id = true;
|
|
|
|
pos->has_build_id = true;
|
|
|
|
}
|
2017-07-06 09:48:11 +08:00
|
|
|
nsinfo__mountns_exit(&nsc);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return have_build_id;
|
|
|
|
}
|
|
|
|
|
2015-06-02 02:40:01 +08:00
|
|
|
void __dsos__add(struct dsos *dsos, struct dso *dso)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2014-09-30 04:07:28 +08:00
|
|
|
list_add_tail(&dso->node, &dsos->head);
|
2015-06-02 02:40:01 +08:00
|
|
|
__dso__findlink_by_longname(&dsos->root, dso, NULL);
|
2015-06-02 22:53:26 +08:00
|
|
|
/*
|
|
|
|
* It is now in the linked list, grab a reference, then garbage collect
|
|
|
|
* this when needing memory, by looking at LRU dso instances in the
|
|
|
|
* list with atomic_read(&dso->refcnt) == 1, i.e. no references
|
|
|
|
* anywhere besides the one for the list, do, under a lock for the
|
|
|
|
* list: remove it from the list, then a dso__put(), that probably will
|
|
|
|
* be the last and will then call dso__delete(), end of life.
|
|
|
|
*
|
|
|
|
* That, or at the end of the 'struct machine' lifetime, when all
|
|
|
|
* 'struct dso' instances will be removed from the list, in
|
|
|
|
* dsos__exit(), if they have no other reference from some other data
|
|
|
|
* structure.
|
|
|
|
*
|
|
|
|
* E.g.: after processing a 'perf.data' file and storing references
|
|
|
|
* to objects instantiated while processing events, we will have
|
|
|
|
* references to the 'thread', 'map', 'dso' structs all from 'struct
|
|
|
|
* hist_entry' instances, but we may not need anything not referenced,
|
|
|
|
* so we might as well call machines__exit()/machines__delete() and
|
|
|
|
* garbage collect it.
|
|
|
|
*/
|
|
|
|
dso__get(dso);
|
2015-06-02 02:40:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void dsos__add(struct dsos *dsos, struct dso *dso)
|
|
|
|
{
|
2017-04-05 00:15:04 +08:00
|
|
|
down_write(&dsos->lock);
|
2015-06-02 02:40:01 +08:00
|
|
|
__dsos__add(dsos, dso);
|
2017-04-05 00:15:04 +08:00
|
|
|
up_write(&dsos->lock);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
2015-06-02 02:40:01 +08:00
|
|
|
struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
|
|
|
struct dso *pos;
|
|
|
|
|
perf symbols: Fix vdso list searching
When "perf record" was used on a large machine with a lot of CPUs, the
perf post-processing time (the time after the workload was done until
the perf command itself exited) could take a lot of minutes and even
hours depending on how large the resulting perf.data file was.
While running AIM7 1500-user high_systime workload on a 80-core x86-64
system with a 3.9 kernel (with only the -s -a options used), the
workload itself took about 2 minutes to run and the perf.data file had a
size of 1108.746 MB. However, the post-processing step took more than 10
minutes.
With a gprof-profiled perf binary, the time spent by perf was as
follows:
% cumulative self self total
time seconds seconds calls s/call s/call name
96.90 822.10 822.10 192156 0.00 0.00 dsos__find
0.81 828.96 6.86 172089958 0.00 0.00 rb_next
0.41 832.44 3.48 48539289 0.00 0.00 rb_erase
So 97% (822 seconds) of the time was spent in a single dsos_find()
function. After analyzing the call-graph data below:
-----------------------------------------------
0.00 822.12 192156/192156 map__new [6]
[7] 96.9 0.00 822.12 192156 vdso__dso_findnew [7]
822.10 0.00 192156/192156 dsos__find [8]
0.01 0.00 192156/192156 dsos__add [62]
0.01 0.00 192156/192366 dso__new [61]
0.00 0.00 1/45282525 memdup [31]
0.00 0.00 192156/192230 dso__set_long_name [91]
-----------------------------------------------
822.10 0.00 192156/192156 vdso__dso_findnew [7]
[8] 96.9 822.10 0.00 192156 dsos__find [8]
-----------------------------------------------
It was found that the vdso__dso_findnew() function failed to locate
VDSO__MAP_NAME ("[vdso]") in the dso list and have to insert a new
entry at the end for 192156 times. This problem is due to the fact that
there are 2 types of name in the dso entry - short name and long name.
The initial dso__new() adds "[vdso]" to both the short and long names.
After that, vdso__dso_findnew() modifies the long name to something
like /tmp/perf-vdso.so-NoXkDj. The dsos__find() function only compares
the long name. As a result, the same vdso entry is duplicated many
time in the dso list. This bug increases memory consumption as well
as slows the symbol processing time to a crawl.
To resolve this problem, the dsos__find() function interface was
modified to enable searching either the long name or the short
name. The vdso__dso_findnew() will now search only the short name
while the other call sites search for the long name as before.
With this change, the cpu time of perf was reduced from 848.38s to
15.77s and dsos__find() only accounted for 0.06% of the total time.
0.06 15.73 0.01 192151 0.00 0.00 dsos__find
Signed-off-by: Waiman Long <Waiman.Long@hp.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Cc: "Chandramouleeswaran, Aswin" <aswin@hp.com>
Cc: "Norton, Scott J" <scott.norton@hp.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1368110568-64714-1-git-send-email-Waiman.Long@hp.com
[ replaced TRUE/FALSE with stdbool.h equivalents, fixing builds where
those macros are not present (NO_LIBPYTHON=1 NO_LIBPERL=1), fix from Jiri Olsa ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-05-09 22:42:48 +08:00
|
|
|
if (cmp_short) {
|
2014-09-30 04:07:28 +08:00
|
|
|
list_for_each_entry(pos, &dsos->head, node)
|
perf symbols: Fix vdso list searching
When "perf record" was used on a large machine with a lot of CPUs, the
perf post-processing time (the time after the workload was done until
the perf command itself exited) could take a lot of minutes and even
hours depending on how large the resulting perf.data file was.
While running AIM7 1500-user high_systime workload on a 80-core x86-64
system with a 3.9 kernel (with only the -s -a options used), the
workload itself took about 2 minutes to run and the perf.data file had a
size of 1108.746 MB. However, the post-processing step took more than 10
minutes.
With a gprof-profiled perf binary, the time spent by perf was as
follows:
% cumulative self self total
time seconds seconds calls s/call s/call name
96.90 822.10 822.10 192156 0.00 0.00 dsos__find
0.81 828.96 6.86 172089958 0.00 0.00 rb_next
0.41 832.44 3.48 48539289 0.00 0.00 rb_erase
So 97% (822 seconds) of the time was spent in a single dsos_find()
function. After analyzing the call-graph data below:
-----------------------------------------------
0.00 822.12 192156/192156 map__new [6]
[7] 96.9 0.00 822.12 192156 vdso__dso_findnew [7]
822.10 0.00 192156/192156 dsos__find [8]
0.01 0.00 192156/192156 dsos__add [62]
0.01 0.00 192156/192366 dso__new [61]
0.00 0.00 1/45282525 memdup [31]
0.00 0.00 192156/192230 dso__set_long_name [91]
-----------------------------------------------
822.10 0.00 192156/192156 vdso__dso_findnew [7]
[8] 96.9 822.10 0.00 192156 dsos__find [8]
-----------------------------------------------
It was found that the vdso__dso_findnew() function failed to locate
VDSO__MAP_NAME ("[vdso]") in the dso list and have to insert a new
entry at the end for 192156 times. This problem is due to the fact that
there are 2 types of name in the dso entry - short name and long name.
The initial dso__new() adds "[vdso]" to both the short and long names.
After that, vdso__dso_findnew() modifies the long name to something
like /tmp/perf-vdso.so-NoXkDj. The dsos__find() function only compares
the long name. As a result, the same vdso entry is duplicated many
time in the dso list. This bug increases memory consumption as well
as slows the symbol processing time to a crawl.
To resolve this problem, the dsos__find() function interface was
modified to enable searching either the long name or the short
name. The vdso__dso_findnew() will now search only the short name
while the other call sites search for the long name as before.
With this change, the cpu time of perf was reduced from 848.38s to
15.77s and dsos__find() only accounted for 0.06% of the total time.
0.06 15.73 0.01 192151 0.00 0.00 dsos__find
Signed-off-by: Waiman Long <Waiman.Long@hp.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Cc: "Chandramouleeswaran, Aswin" <aswin@hp.com>
Cc: "Norton, Scott J" <scott.norton@hp.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1368110568-64714-1-git-send-email-Waiman.Long@hp.com
[ replaced TRUE/FALSE with stdbool.h equivalents, fixing builds where
those macros are not present (NO_LIBPYTHON=1 NO_LIBPERL=1), fix from Jiri Olsa ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-05-09 22:42:48 +08:00
|
|
|
if (strcmp(pos->short_name, name) == 0)
|
|
|
|
return pos;
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-06-02 02:40:01 +08:00
|
|
|
return __dso__find_by_longname(&dsos->root, name);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
2015-06-02 02:40:01 +08:00
|
|
|
struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
|
|
|
|
{
|
|
|
|
struct dso *dso;
|
2017-04-05 00:15:04 +08:00
|
|
|
down_read(&dsos->lock);
|
2015-06-02 02:40:01 +08:00
|
|
|
dso = __dsos__find(dsos, name, cmp_short);
|
2017-04-05 00:15:04 +08:00
|
|
|
up_read(&dsos->lock);
|
2015-06-02 02:40:01 +08:00
|
|
|
return dso;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
2015-02-13 05:06:09 +08:00
|
|
|
struct dso *dso = dso__new(name);
|
2012-10-28 05:18:32 +08:00
|
|
|
|
2015-02-13 05:06:09 +08:00
|
|
|
if (dso != NULL) {
|
2015-06-02 02:40:01 +08:00
|
|
|
__dsos__add(dsos, dso);
|
2015-02-13 05:06:09 +08:00
|
|
|
dso__set_basename(dso);
|
2015-11-18 14:40:31 +08:00
|
|
|
/* Put dso here because __dsos_add already got it */
|
|
|
|
dso__put(dso);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
return dso;
|
|
|
|
}
|
|
|
|
|
2015-02-13 05:06:09 +08:00
|
|
|
struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
|
|
|
|
{
|
2015-06-02 02:40:01 +08:00
|
|
|
struct dso *dso = __dsos__find(dsos, name, false);
|
|
|
|
|
|
|
|
return dso ? dso : __dsos__addnew(dsos, name);
|
|
|
|
}
|
2015-02-13 05:06:09 +08:00
|
|
|
|
2015-06-02 02:40:01 +08:00
|
|
|
struct dso *dsos__findnew(struct dsos *dsos, const char *name)
|
|
|
|
{
|
|
|
|
struct dso *dso;
|
2017-04-05 00:15:04 +08:00
|
|
|
down_write(&dsos->lock);
|
2015-06-02 22:53:26 +08:00
|
|
|
dso = dso__get(__dsos__findnew(dsos, name));
|
2017-04-05 00:15:04 +08:00
|
|
|
up_write(&dsos->lock);
|
2015-06-02 02:40:01 +08:00
|
|
|
return dso;
|
2015-02-13 05:06:09 +08:00
|
|
|
}
|
|
|
|
|
2012-10-28 05:18:32 +08:00
|
|
|
size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
|
2012-12-07 20:53:58 +08:00
|
|
|
bool (skip)(struct dso *dso, int parm), int parm)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
|
|
|
struct dso *pos;
|
|
|
|
size_t ret = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(pos, head, node) {
|
2012-12-07 20:53:58 +08:00
|
|
|
if (skip && skip(pos, parm))
|
2012-10-28 05:18:32 +08:00
|
|
|
continue;
|
|
|
|
ret += dso__fprintf_buildid(pos, fp);
|
|
|
|
ret += fprintf(fp, " %s\n", pos->long_name);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t __dsos__fprintf(struct list_head *head, FILE *fp)
|
|
|
|
{
|
|
|
|
struct dso *pos;
|
|
|
|
size_t ret = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(pos, head, node) {
|
2018-04-27 03:52:34 +08:00
|
|
|
ret += dso__fprintf(pos, fp);
|
2012-10-28 05:18:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t dso__fprintf_buildid(struct dso *dso, FILE *fp)
|
|
|
|
{
|
2016-05-11 21:51:59 +08:00
|
|
|
char sbuild_id[SBUILD_ID_SIZE];
|
2012-10-28 05:18:32 +08:00
|
|
|
|
|
|
|
build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
|
|
|
|
return fprintf(fp, "%s", sbuild_id);
|
|
|
|
}
|
|
|
|
|
2018-04-27 03:52:34 +08:00
|
|
|
size_t dso__fprintf(struct dso *dso, FILE *fp)
|
2012-10-28 05:18:32 +08:00
|
|
|
{
|
|
|
|
struct rb_node *nd;
|
|
|
|
size_t ret = fprintf(fp, "dso: %s (", dso->short_name);
|
|
|
|
|
|
|
|
if (dso->short_name != dso->long_name)
|
|
|
|
ret += fprintf(fp, "%s, ", dso->long_name);
|
2018-04-27 03:52:34 +08:00
|
|
|
ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT ");
|
2012-10-28 05:18:32 +08:00
|
|
|
ret += dso__fprintf_buildid(dso, fp);
|
|
|
|
ret += fprintf(fp, ")\n");
|
2018-04-27 03:52:34 +08:00
|
|
|
for (nd = rb_first(&dso->symbols); nd; nd = rb_next(nd)) {
|
2012-10-28 05:18:32 +08:00
|
|
|
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
|
|
|
|
ret += symbol__fprintf(pos, fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2014-07-22 21:17:59 +08:00
|
|
|
|
|
|
|
enum dso_type dso__type(struct dso *dso, struct machine *machine)
|
|
|
|
{
|
|
|
|
int fd;
|
2015-05-21 00:03:41 +08:00
|
|
|
enum dso_type type = DSO__TYPE_UNKNOWN;
|
2014-07-22 21:17:59 +08:00
|
|
|
|
2015-05-21 00:03:41 +08:00
|
|
|
fd = dso__data_get_fd(dso, machine);
|
|
|
|
if (fd >= 0) {
|
|
|
|
type = dso__type_fd(fd);
|
|
|
|
dso__data_put_fd(dso);
|
|
|
|
}
|
2014-07-22 21:17:59 +08:00
|
|
|
|
2015-05-21 00:03:41 +08:00
|
|
|
return type;
|
2014-07-22 21:17:59 +08:00
|
|
|
}
|
2015-03-24 22:49:02 +08:00
|
|
|
|
|
|
|
int dso__strerror_load(struct dso *dso, char *buf, size_t buflen)
|
|
|
|
{
|
|
|
|
int idx, errnum = dso->load_errno;
|
|
|
|
/*
|
|
|
|
* This must have a same ordering as the enum dso_load_errno.
|
|
|
|
*/
|
|
|
|
static const char *dso_load__error_str[] = {
|
|
|
|
"Internal tools/perf/ library error",
|
|
|
|
"Invalid ELF file",
|
|
|
|
"Can not read build id",
|
|
|
|
"Mismatching build id",
|
|
|
|
"Decompression failure",
|
|
|
|
};
|
|
|
|
|
|
|
|
BUG_ON(buflen == 0);
|
|
|
|
|
|
|
|
if (errnum >= 0) {
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 22:56:20 +08:00
|
|
|
const char *err = str_error_r(errnum, buf, buflen);
|
2015-03-24 22:49:02 +08:00
|
|
|
|
|
|
|
if (err != buf)
|
|
|
|
scnprintf(buf, buflen, "%s", err);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errnum < __DSO_LOAD_ERRNO__START || errnum >= __DSO_LOAD_ERRNO__END)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
idx = errnum - __DSO_LOAD_ERRNO__START;
|
|
|
|
scnprintf(buf, buflen, "%s", dso_load__error_str[idx]);
|
|
|
|
return 0;
|
|
|
|
}
|