linux/tools/perf/util/vdso.c
Ian Rogers ee756ef749 perf dso: Add reference count checking and accessor functions
Add reference count checking to struct dso, this can help with
implementing correct reference counting discipline. To avoid
RC_CHK_ACCESS everywhere, add accessor functions for the variables in
struct dso.

The majority of the change is mechanical in nature and not easy to
split up.

Committer testing:

'perf test' up to this patch shows no regressions.

But:

  util/symbol.c: In function ‘dso__load_bfd_symbols’:
  util/symbol.c:1683:9: error: too few arguments to function ‘dso__set_adjust_symbols’
   1683 |         dso__set_adjust_symbols(dso);
        |         ^~~~~~~~~~~~~~~~~~~~~~~
  In file included from util/symbol.c:21:
  util/dso.h:268:20: note: declared here
    268 | static inline void dso__set_adjust_symbols(struct dso *dso, bool val)
        |                    ^~~~~~~~~~~~~~~~~~~~~~~
  make[6]: *** [/home/acme/git/perf-tools-next/tools/build/Makefile.build:106: /tmp/tmp.ZWHbQftdN6/util/symbol.o] Error 1
    MKDIR   /tmp/tmp.ZWHbQftdN6/tests/workloads/
  make[6]: *** Waiting for unfinished jobs....

This was updated:

  -       symbols__fixup_end(&dso->symbols, false);
  -       symbols__fixup_duplicate(&dso->symbols);
  -       dso->adjust_symbols = 1;
  +       symbols__fixup_end(dso__symbols(dso), false);
  +       symbols__fixup_duplicate(dso__symbols(dso));
  +       dso__set_adjust_symbols(dso);

But not build tested with BUILD_NONDISTRO and libbfd devel files installed
(binutils-devel on fedora).

Add the missing argument:

   	symbols__fixup_end(dso__symbols(dso), false);
   	symbols__fixup_duplicate(dso__symbols(dso));
  -	dso__set_adjust_symbols(dso);
  +	dso__set_adjust_symbols(dso, true);

Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Ben Gainey <ben.gainey@arm.com>
Cc: Changbin Du <changbin.du@huawei.com>
Cc: Chengen Du <chengen.du@canonical.com>
Cc: Colin Ian King <colin.i.king@gmail.com>
Cc: Dima Kogan <dima@secretsauce.net>
Cc: Ilkka Koskinen <ilkka@os.amperecomputing.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: K Prateek Nayak <kprateek.nayak@amd.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Li Dong <lidong@vivo.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paran Lee <p4ranlee@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Song Liu <song@kernel.org>
Cc: Sun Haiyong <sunhaiyong@loongson.cn>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Tiezhu Yang <yangtiezhu@loongson.cn>
Cc: Yanteng Si <siyanteng@loongson.cn>
Cc: zhaimingbing <zhaimingbing@cmss.chinamobile.com>
Link: https://lore.kernel.org/r/20240504213803.218974-6-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2024-05-06 15:28:49 -03:00

368 lines
7.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/kernel.h>
#include "vdso.h"
#include "dso.h"
#include <internal/lib.h>
#include "map.h"
#include "symbol.h"
#include "machine.h"
#include "thread.h"
#include "linux/string.h"
#include <linux/zalloc.h>
#include "debug.h"
/*
* Include definition of find_map() also used in perf-read-vdso.c for
* building perf-read-vdso32 and perf-read-vdsox32.
*/
#include "find-map.c"
#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"
struct vdso_file {
bool found;
bool error;
char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
const char *dso_name;
const char *read_prog;
};
struct vdso_info {
struct vdso_file vdso;
#if BITS_PER_LONG == 64
struct vdso_file vdso32;
struct vdso_file vdsox32;
#endif
};
static struct vdso_info *vdso_info__new(void)
{
static const struct vdso_info vdso_info_init = {
.vdso = {
.temp_file_name = VDSO__TEMP_FILE_NAME,
.dso_name = DSO__NAME_VDSO,
},
#if BITS_PER_LONG == 64
.vdso32 = {
.temp_file_name = VDSO__TEMP_FILE_NAME,
.dso_name = DSO__NAME_VDSO32,
.read_prog = "perf-read-vdso32",
},
.vdsox32 = {
.temp_file_name = VDSO__TEMP_FILE_NAME,
.dso_name = DSO__NAME_VDSOX32,
.read_prog = "perf-read-vdsox32",
},
#endif
};
return memdup(&vdso_info_init, sizeof(vdso_info_init));
}
static char *get_file(struct vdso_file *vdso_file)
{
char *vdso = NULL;
char *buf = NULL;
void *start, *end;
size_t size;
int fd;
if (vdso_file->found)
return vdso_file->temp_file_name;
if (vdso_file->error || find_map(&start, &end, VDSO__MAP_NAME))
return NULL;
size = end - start;
buf = memdup(start, size);
if (!buf)
return NULL;
fd = mkstemp(vdso_file->temp_file_name);
if (fd < 0)
goto out;
if (size == (size_t) write(fd, buf, size))
vdso = vdso_file->temp_file_name;
close(fd);
out:
free(buf);
vdso_file->found = (vdso != NULL);
vdso_file->error = !vdso_file->found;
return vdso;
}
void machine__exit_vdso(struct machine *machine)
{
struct vdso_info *vdso_info = machine->vdso_info;
if (!vdso_info)
return;
if (vdso_info->vdso.found)
unlink(vdso_info->vdso.temp_file_name);
#if BITS_PER_LONG == 64
if (vdso_info->vdso32.found)
unlink(vdso_info->vdso32.temp_file_name);
if (vdso_info->vdsox32.found)
unlink(vdso_info->vdsox32.temp_file_name);
#endif
zfree(&machine->vdso_info);
}
static struct dso *__machine__addnew_vdso(struct machine *machine, const char *short_name,
const char *long_name)
{
struct dso *dso;
dso = dso__new(short_name);
if (dso != NULL) {
__dsos__add(&machine->dsos, dso);
dso__set_long_name(dso, long_name, false);
}
return dso;
}
struct machine__thread_dso_type_maps_cb_args {
struct machine *machine;
enum dso_type dso_type;
};
static int machine__thread_dso_type_maps_cb(struct map *map, void *data)
{
struct machine__thread_dso_type_maps_cb_args *args = data;
struct dso *dso = map__dso(map);
if (!dso || dso__long_name(dso)[0] != '/')
return 0;
args->dso_type = dso__type(dso, args->machine);
return (args->dso_type != DSO__TYPE_UNKNOWN) ? 1 : 0;
}
static enum dso_type machine__thread_dso_type(struct machine *machine,
struct thread *thread)
{
struct machine__thread_dso_type_maps_cb_args args = {
.machine = machine,
.dso_type = DSO__TYPE_UNKNOWN,
};
maps__for_each_map(thread__maps(thread), machine__thread_dso_type_maps_cb, &args);
return args.dso_type;
}
#if BITS_PER_LONG == 64
static int vdso__do_copy_compat(FILE *f, int fd)
{
char buf[4096];
size_t count;
while (1) {
count = fread(buf, 1, sizeof(buf), f);
if (ferror(f))
return -errno;
if (feof(f))
break;
if (count && writen(fd, buf, count) != (ssize_t)count)
return -errno;
}
return 0;
}
static int vdso__copy_compat(const char *prog, int fd)
{
FILE *f;
int err;
f = popen(prog, "r");
if (!f)
return -errno;
err = vdso__do_copy_compat(f, fd);
if (pclose(f) == -1)
return -errno;
return err;
}
static int vdso__create_compat_file(const char *prog, char *temp_name)
{
int fd, err;
fd = mkstemp(temp_name);
if (fd < 0)
return -errno;
err = vdso__copy_compat(prog, fd);
if (close(fd) == -1)
return -errno;
return err;
}
static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
{
int err;
if (vdso_file->found)
return vdso_file->temp_file_name;
if (vdso_file->error)
return NULL;
err = vdso__create_compat_file(vdso_file->read_prog,
vdso_file->temp_file_name);
if (err) {
pr_err("%s failed, error %d\n", vdso_file->read_prog, err);
vdso_file->error = true;
return NULL;
}
vdso_file->found = true;
return vdso_file->temp_file_name;
}
static struct dso *__machine__findnew_compat(struct machine *machine,
struct vdso_file *vdso_file)
{
const char *file_name;
struct dso *dso;
dso = dsos__find(&machine->dsos, vdso_file->dso_name, true);
if (dso)
return dso;
file_name = vdso__get_compat_file(vdso_file);
if (!file_name)
return NULL;
return __machine__addnew_vdso(machine, vdso_file->dso_name, file_name);
}
static int __machine__findnew_vdso_compat(struct machine *machine,
struct thread *thread,
struct vdso_info *vdso_info,
struct dso **dso)
{
enum dso_type dso_type;
dso_type = machine__thread_dso_type(machine, thread);
#ifndef HAVE_PERF_READ_VDSO32
if (dso_type == DSO__TYPE_32BIT)
return 0;
#endif
#ifndef HAVE_PERF_READ_VDSOX32
if (dso_type == DSO__TYPE_X32BIT)
return 0;
#endif
switch (dso_type) {
case DSO__TYPE_32BIT:
*dso = __machine__findnew_compat(machine, &vdso_info->vdso32);
return 1;
case DSO__TYPE_X32BIT:
*dso = __machine__findnew_compat(machine, &vdso_info->vdsox32);
return 1;
case DSO__TYPE_UNKNOWN:
case DSO__TYPE_64BIT:
default:
return 0;
}
}
#endif
static struct dso *machine__find_vdso(struct machine *machine,
struct thread *thread)
{
struct dso *dso = NULL;
enum dso_type dso_type;
dso_type = machine__thread_dso_type(machine, thread);
switch (dso_type) {
case DSO__TYPE_32BIT:
dso = dsos__find(&machine->dsos, DSO__NAME_VDSO32, true);
if (!dso) {
dso = dsos__find(&machine->dsos, DSO__NAME_VDSO,
true);
if (dso && dso_type != dso__type(dso, machine))
dso = NULL;
}
break;
case DSO__TYPE_X32BIT:
dso = dsos__find(&machine->dsos, DSO__NAME_VDSOX32, true);
break;
case DSO__TYPE_64BIT:
case DSO__TYPE_UNKNOWN:
default:
dso = dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
break;
}
return dso;
}
struct dso *machine__findnew_vdso(struct machine *machine,
struct thread *thread)
{
struct vdso_info *vdso_info;
struct dso *dso = NULL;
char *file;
if (!machine->vdso_info)
machine->vdso_info = vdso_info__new();
vdso_info = machine->vdso_info;
if (!vdso_info)
return NULL;
dso = machine__find_vdso(machine, thread);
if (dso)
return dso;
#if BITS_PER_LONG == 64
if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso))
return dso;
#endif
dso = dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
if (dso)
return dso;
file = get_file(&vdso_info->vdso);
if (!file)
return NULL;
return __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
}
bool dso__is_vdso(struct dso *dso)
{
return !strcmp(dso__short_name(dso), DSO__NAME_VDSO) ||
!strcmp(dso__short_name(dso), DSO__NAME_VDSO32) ||
!strcmp(dso__short_name(dso), DSO__NAME_VDSOX32);
}