Skip to contentSkip to conversation

perf/sdt: SDT events listing/probing

PATCH Superseded Hemant Kumar 4 participants 20 messages Oct 10, 2014 – Oct 24, 2014 perf events

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v3 cover perf/sdt: SDT events listing/probing

This patchset helps in listing dtrace style markers(SDT) present in user space applications through perf.
Notes/markers are placed at important places by the
developers. They have a negligible overhead when not enabled.
We can enable them and probe at these places and find some important information like the arguments' values, etc.

We have lots of applications which use SDT markers today, like:
Postgresql, MySql, Mozilla, Perl, Python, Java, Ruby, libvirt, QEMU, glib

To add SDT markers into user applications:
We need to have this header sys/sdt.h present.
sys/sdt.h used is version 3.
If not present, install systemtap-sdt-devel package (for fedora-20). With this patchset,
- Use perf sdt-cache --add to add SDT events to a cache.

 # perf sdt-cache --add ./user_app

4 SDT events added for /home/user/user_app

- Use perf sdt-cache --del to remove SDT events from the cache>

 # perf sdt-cache --del ./user_app

4 events removed for /home/user/user_app

- Dump the cache onto stdout using perf sdt-cache --dump:

 # perf sdt-cache --dump
  /home/user/user_app :
  %user_app:foo_start
  %user_app:fun_start

- To probe and trace an SDT event :

  # perf record -e %libc:setjmp -aR sleep 10

The support for perf to sdt events has undergone a lot of changes since it was introduced. After a lot of discussions (https://lkml.org/lkml/2014/7/20/284),the
patchset is subdivided for ease of review.
Previously, a patchset supporting "perf list sdt" was posted, but it wasn't very helpful, since only listing SDT events for an ELF won't help.
This patchset aims at adding a sub-command "sdt-cache" to perf to manage a cache for management of the SDT events.

This link shows an example of marker probing with Systemtap:
https://sourceware.org/systemtap/wiki/AddingUserSpaceProbingToApps

This link provides important info regarding SDT notes:
http://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation

- Markers in binaries :
These SDT markers are present in the ELF in the section named ".note.stapsdt".
Here, the name of the marker, its provider, type, location, base address, semaphore address.
We can retrieve these values using the members name_off and desc_off in Nhdr structure. If these are not enabled, they are present in the ELF as nop.

Changes since v2 :
- Changed list_head to hlist_head in the hash_tables.
- Changed the cache reading and parsing methods.
- Implemented other necessary changes and modifications suggested by Masami and Namhyung.

TODO:
- Add support to add most of the SDT events on a system to the cache.
- Recognizing arguments and support to probe on them.
---

Hemant Kumar (5):
perf/sdt: ELF support for SDT
perf/sdt: Add SDT events into a cache perf/sdt: Show SDT cache contents
perf/sdt: Delete SDT events from cache perf/sdt: Add support to perf record to trace SDT events

 tools/perf/Documentation/perf-sdt-cache.txt |   36 +
 tools/perf/Makefile.perf                    |    4 
 tools/perf/builtin-record.c                 |   22 +
 tools/perf/builtin-sdt-cache.c              |  112 +++
 tools/perf/builtin.h                        |    1 
 tools/perf/command-list.txt                 |    1 
 tools/perf/perf.c                           |    1 
 tools/perf/util/parse-events.c              |    6 
 tools/perf/util/parse-events.h              |    7 
 tools/perf/util/probe-event.c               |  121 +++-
 tools/perf/util/probe-event.h               |    7 
 tools/perf/util/probe-finder.c              |    3 
 tools/perf/util/sdt.c                       |  866 +++++++++++++++++++++++++++
 tools/perf/util/symbol-elf.c                |  257 ++++++++
 tools/perf/util/symbol.h                    |   23 +
 15 files changed, 1444 insertions(+), 23 deletions(-)
 create mode 100644 tools/perf/Documentation/perf-sdt-cache.txt
 create mode 100644 tools/perf/builtin-sdt-cache.c
 create mode 100644 tools/perf/util/sdt.c
#1 lore raw

PATCH v3 1/5 perf/sdt: ELF support for SDT

This patch serves the initial support to identify and list SDT events in binaries. When programs containing SDT markers are compiled, gcc with the help of assembler directives identifies them and places them in the section ".note.stapsdt". To find these markers from the binaries, one needs to traverse through this section and parse the relevant details like the name, type and location of the marker. Also, the original location could be skewed due to the effect of prelinking. If that is the case, the locations need to be adjusted.

The functions in this patch open a given ELF, find out the SDT section, parse the relevant details, adjust the location (if necessary) and populate them in a list.

---
 tools/perf/util/symbol-elf.c |  257 ++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/symbol.h     |   21 +++
 2 files changed, 278 insertions(+)
tools/perf/util/symbol-elf.c +257 -0
index 1e23a5b..e403df6 100644
@@ -1677,6 +1677,263 @@ void kcore_extract__delete(struct kcore_extract *kce)
 	unlink(kce->extract_filename);
 }
 
+/**
+ * populate_sdt_note : Parse raw data and identify SDT note
+ * @elf: elf of the opened file
+ * @data: raw data of a section with description offset applied
+ * @len: note description size
+ * @type: type of the note
+ * @sdt_notes: List to add the SDT note
+ *
+ * Responsible for parsing the @data in section .note.stapsdt in @elf and
+ * if its an SDT note, it appends to @sdt_notes list.
+ */
+static int populate_sdt_note(Elf **elf, const char *data, size_t len, int type,
+			     struct list_head *sdt_notes)
+{
+	const char *provider, *name;
+	struct sdt_note *tmp = NULL;
+	GElf_Ehdr ehdr;
+	GElf_Addr base_off = 0;
+	GElf_Shdr shdr;
+	int i, ret = -EINVAL;
+
+	union {
+		Elf64_Addr a64[3];
+		Elf32_Addr a32[3];
+	} buf;
+
+	Elf_Data dst = {
+		.d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
+		.d_size = gelf_fsize((*elf), ELF_T_ADDR, 3, EV_CURRENT),
+		.d_off = 0, .d_align = 0
+	};
+	Elf_Data src = {
+		.d_buf = (void *) data, .d_type = ELF_T_ADDR,
+		.d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0,
+		.d_align = 0
+	};
+
+	/* Check the type of each of the notes */
+	if (type != SDT_NOTE_TYPE)
+		goto out_err;
+
+	tmp = (struct sdt_note *)calloc(1, sizeof(struct sdt_note));
+	if (!tmp) {
+		ret = -ENOMEM;
+		goto out_err;
+	}
+
+	INIT_LIST_HEAD(&tmp->note_list);
+
+	if (len < dst.d_size + 3)
+		goto out_free_note;
+
+	/* Translation from file representation to memory representation */
+	if (gelf_xlatetom(*elf, &dst, &src,
+			  elf_getident(*elf, NULL)[EI_DATA]) == NULL) {
+		pr_err("gelf_xlatetom : %s\n", elf_errmsg(-1));
+		goto out_free_note;
+	}
+
+	/* Populate the fields of sdt_note */
+	provider = data + dst.d_size;
+
+	name = (const char *)memchr(provider, '\0', data + len - provider);
+	if (name++ == NULL)
+		goto out_free_note;
+
+	tmp->provider = strdup(provider);
+	if (!tmp->provider) {
+		ret = -ENOMEM;
+		goto out_free_note;
+	}
+	tmp->name = strdup(name);
+	if (!tmp->name) {
+		ret = -ENOMEM;
+		goto out_free_prov;
+	}
+
+	/* Obtain the addresses */
+	if (gelf_getclass(*elf) == ELFCLASS32) {
+		for (i = 0; i < 3; i++)
+			tmp->addr.a32[i] = buf.a32[i];
+		tmp->bit32 = true;
+	} else {
+		for (i = 0; i < 3; i++)
+			tmp->addr.a64[i] = buf.a64[i];
+		tmp->bit32 = false;
+	}
+
+	/* Now Adjust the prelink effect */
+	if (!gelf_getehdr(*elf, &ehdr)) {
+		pr_debug("%s : cannot get elf header.\n", __func__);
+		ret = -EBADF;
+		goto out_free_name;
+	}
+
+	/*
+	 * Find out the .stapsdt.base section.
+	 * This scn will help us to handle prelinking (if present).
+	 * Compare the retrieved file offset of the base section with the
+	 * base address in the description of the SDT note. If its different,
+	 * then accordingly, adjust the note location.
+	 */
+	if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) {
+		base_off = shdr.sh_offset;
+		if (base_off) {
+			if (tmp->bit32)
+				tmp->addr.a32[0] = tmp->addr.a32[0] + base_off -
+					tmp->addr.a32[1];
+			else
+				tmp->addr.a64[0] = tmp->addr.a64[0] + base_off -
+					tmp->addr.a64[1];
+		}
+	}
+
+	list_add_tail(&tmp->note_list, sdt_notes);
+	return 0;
+
+out_free_name:
+	free(tmp->name);
+out_free_prov:
+	free(tmp->provider);
+out_free_note:
+	free(tmp);
+out_err:
+	return ret;
+}
+
+/**
+ * construct_sdt_notes_list : constructs a list of SDT notes
+ * @elf : elf to look into
+ * @sdt_notes : empty list_head
+ *
+ * Scans the sections in 'elf' for the section
+ * .note.stapsdt. It, then calls populate_sdt_note to find
+ * out the SDT events and populates the 'sdt_notes'.
+ */
+static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes)
+{
+	GElf_Ehdr ehdr;
+	Elf_Scn *scn = NULL;
+	Elf_Data *data;
+	GElf_Shdr shdr;
+	size_t shstrndx, next;
+	GElf_Nhdr nhdr;
+	size_t name_off, desc_off, offset;
+	int ret = 0;
+
+	if (gelf_getehdr(elf, &ehdr) == NULL) {
+		ret = -EBADF;
+		goto out_ret;
+	}
+	if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
+		ret = -EBADF;
+		goto out_ret;
+	}
+
+	/* Look for the required section */
+	scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL);
+	if (!scn) {
+		ret = -ENOENT;
+		goto out_ret;
+	}
+
+	if (!(shdr.sh_type == SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {
+		ret = -ENOENT;
+		goto out_ret;
+	}
+
+	data = elf_getdata(scn, NULL);
+
+	/* Get the SDT notes */
+	for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off,
+					      &desc_off)) > 0; offset = next) {
+		if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) &&
+		    !memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
+			    sizeof(SDT_NOTE_NAME))) {
+			ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off),
+						nhdr.n_descsz, nhdr.n_type,
+						sdt_notes);
+			if (ret < 0)
+				goto out_ret;
+		}
+	}
+	if (list_empty(sdt_notes))
+		ret = -ENOENT;
+
+out_ret:
+	return ret;
+}
+
+/**
+ * get_sdt_note_list : Wrapper to construct a list of sdt notes
+ * @head : empty list_head
+ * @target : file to find SDT notes from
+ *
+ * This opens the file, initializes
+ * the ELF and then calls construct_sdt_notes_list.
+ */
+int get_sdt_note_list(struct list_head *head, const char *target)
+{
+	Elf *elf;
+	int fd, ret;
+
+	fd = open(target, O_RDONLY);
+	if (fd < 0)
+		return -EBADF;
+
+	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+	if (!elf) {
+		ret = -EBADF;
+		goto out_close;
+	}
+	ret = construct_sdt_notes_list(elf, head);
+	elf_end(elf);
+out_close:
+	close(fd);
+	return ret;
+}
+
+/**
+ * cleanup_sdt_note_list : free the sdt notes' list
+ * @sdt_notes: sdt notes' list
+ *
+ * Free up the SDT notes in @sdt_notes.
+ * Returns the number of SDT notes free'd.
+ */
+int cleanup_sdt_note_list(struct list_head *sdt_notes)
+{
+	struct sdt_note *tmp, *pos;
+	int nr_free = 0;
+
+	list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) {
+		list_del(&pos->note_list);
+		free(pos->name);
+		free(pos->provider);
+		free(pos);
+		nr_free++;
+	}
+	return nr_free;
+}
+
+/**
+ * sdt_notes__get_count: Counts the number of sdt events
+ * @start: list_head to sdt_notes list
+ *
+ * Returns the number of SDT notes in a list
+ */
+int sdt_notes__get_count(struct list_head *start)
+{
+	struct sdt_note *sdt_ptr;
+	int count = 0;
+
+	list_for_each_entry(sdt_ptr, start, note_list)
+		count++;
+	return count;
+}
+
 void symbol__elf_init(void)
 {
 	elf_version(EV_CURRENT);
tools/perf/util/symbol.h +21 -0
index bec4b7b..23d3d41 100644
@@ -313,4 +313,25 @@ int compare_proc_modules(const char *from, const char *to);
 int setup_list(struct strlist **list, const char *list_str,
 	       const char *list_name);
 
+/* structure containing an SDT note's info */
+struct sdt_note {
+	char *name;			/* name of the note*/
+	char *provider;			/* provider name */
+	bool bit32;			/* whether the location is 32 bits? */
+	union {				/* location, base and semaphore addrs */
+		Elf64_Addr a64[3];
+		Elf32_Addr a32[3];
+	} addr;
+	struct list_head note_list;	/* SDT notes' list */
+};
+
+int get_sdt_note_list(struct list_head *head, const char *target);
+int cleanup_sdt_note_list(struct list_head *sdt_notes);
+int sdt_notes__get_count(struct list_head *start);
+
+#define SDT_BASE_SCN ".stapsdt.base"
+#define SDT_NOTE_SCN  ".note.stapsdt"
+#define SDT_NOTE_TYPE 3
+#define SDT_NOTE_NAME "stapsdt"
+
 #endif /* __PERF_SYMBOL */
maintainer Acked-by on v3 1/5
#2 lore raw

Re: [PATCH v3 1/5] perf/sdt: ELF support for SDT

Hi Hemant,

Quoted 8 lines · Hemant Kumar

This patch serves the initial support to identify and list SDT events in binaries. When programs containing SDT markers are compiled, gcc with the help of assembler directives identifies them and places them in the section ".note.stapsdt". To find these markers from the binaries, one needs to traverse through this section and parse the relevant details like the name, type and location of the marker. Also, the original location could be skewed due to the effect of prelinking. If that is the case, the locations need to be adjusted.

Quoted 4 lines

The functions in this patch open a given ELF, find out the SDT section, parse the relevant details, adjust the location (if necessary) and populate them in a list.

Just a nitpick below..

Quoted 18 lines
tools/perf/util/symbol-elf.c +18 -0
+static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes)
+{
+	GElf_Ehdr ehdr;
+	Elf_Scn *scn = NULL;
+	Elf_Data *data;
+	GElf_Shdr shdr;
+	size_t shstrndx, next;
+	GElf_Nhdr nhdr;
+	size_t name_off, desc_off, offset;
+	int ret = 0;
+
+	if (gelf_getehdr(elf, &ehdr) == NULL) {
+		ret = -EBADF;
+		goto out_ret;
+	}
+	if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
+		ret = -EBADF;
+		goto out_ret;

tools/perf/util/symbol-elf.c void kcore_extract__delete(struct kcore_extract *kce) · line 1843

Quoted 10 lines
tools/perf/util/symbol-elf.c +10 -0
+	}
+
+	/* Look for the required section */
+	scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL);
+	if (!scn) {
+		ret = -ENOENT;
+		goto out_ret;
+	}
+
+	if (!(shdr.sh_type == SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {

I think the below is more readable:

	if ((shdr.sh_type != SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {

Other than that, looks good to me!

Thanks,
Namhyung

Quoted 25 lines
tools/perf/util/symbol-elf.c +25 -0
+		ret = -ENOENT;
+		goto out_ret;
+	}
+
+	data = elf_getdata(scn, NULL);
+
+	/* Get the SDT notes */
+	for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off,
+					      &desc_off)) > 0; offset = next) {
+		if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) &&
+		    !memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
+			    sizeof(SDT_NOTE_NAME))) {
+			ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off),
+						nhdr.n_descsz, nhdr.n_type,
+						sdt_notes);
+			if (ret < 0)
+				goto out_ret;
+		}
+	}
+	if (list_empty(sdt_notes))
+		ret = -ENOENT;
+
+out_ret:
+	return ret;
+}
#3 lore raw

Re: [PATCH v3 1/5] perf/sdt: ELF support for SDT

Quoted 41 lines · Namhyung Kim

Hi Hemant,

Quoted 8 lines · Hemant Kumar

This patch serves the initial support to identify and list SDT events in binaries. When programs containing SDT markers are compiled, gcc with the help of assembler directives identifies them and places them in the section ".note.stapsdt". To find these markers from the binaries, one needs to traverse through this section and parse the relevant details like the name, type and location of the marker. Also, the original location could be skewed due to the effect of prelinking. If that is the case, the locations need to be adjusted.

Quoted 4 lines

The functions in this patch open a given ELF, find out the SDT section, parse the relevant details, adjust the location (if necessary) and populate them in a list.

Just a nitpick below..

Quoted 21 lines
tools/perf/util/symbol-elf.c +21 -0
+static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes)
+{
+	GElf_Ehdr ehdr;
+	Elf_Scn *scn = NULL;
+	Elf_Data *data;
+	GElf_Shdr shdr;
+	size_t shstrndx, next;
+	GElf_Nhdr nhdr;
+	size_t name_off, desc_off, offset;
+	int ret = 0;
+
+	if (gelf_getehdr(elf, &ehdr) == NULL) {
+		ret = -EBADF;
+		goto out_ret;
+	}
+	if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
+		ret = -EBADF;
+		goto out_ret;
+	}
+
+	/* Look for the required section */
Quoted 8 lines

tools/perf/util/symbol-elf.c void kcore_extract__delete(struct kcore_extract *kce) · line 1843

Quoted 7 lines
tools/perf/util/symbol-elf.c +7 -0
+	scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL);
+	if (!scn) {
+		ret = -ENOENT;
+		goto out_ret;
+	}
+
+	if (!(shdr.sh_type == SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {

I think the below is more readable:

Yes.

Quoted 2 lines
	if ((shdr.sh_type != SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {

Will change to this.

Quoted 32 lines

Other than that, looks good to me!

Thanks,
Namhyung

Quoted 25 lines
tools/perf/util/symbol-elf.c +25 -0
+		ret = -ENOENT;
+		goto out_ret;
+	}
+
+	data = elf_getdata(scn, NULL);
+
+	/* Get the SDT notes */
+	for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off,
+					      &desc_off)) > 0; offset = next) {
+		if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) &&
+		    !memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
+			    sizeof(SDT_NOTE_NAME))) {
+			ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off),
+						nhdr.n_descsz, nhdr.n_type,
+						sdt_notes);
+			if (ret < 0)
+				goto out_ret;
+		}
+	}
+	if (list_empty(sdt_notes))
+		ret = -ENOENT;
+
+out_ret:
+	return ret;
+}
Signature · 2 lines
Thanks,
Hemant Kumar
#4 lore raw

PATCH v3 2/5 perf/sdt: Add SDT events into a cache

In reply to Hemant Kumar

This patch adds a new sub-command to perf : sdt-cache. sdt-cache command can be used to add SDT events.
When user invokes "perf sdt-cache add <file-name>", a hash table/list is created named as file_hash list. A typical entry in a file_hash table looks like:

                file hash      file_sdt_ent     file_sdt_ent
                |---------|   ---------------   -------------
                | hlist ==|===|=>file_list =|===|=>file_list=|==..
    key = 644 =>|         |   | sbuild_id   |   | sbuild_id  |
                |---------|   | name        |   | name       |
                |         |   | sdt_list    |   | sdt_list   |
    key = 645 =>| hlist   |   |   ||        |   |    ||      |
                |---------|   ---------------   --------------

|| || || Connected to SDT notes
---------------
| note_list |
| name |sdt_note
| provider |
-----||--------
connected to other SDT notes

Each entry of the file_hash table is an hlist which connects to file_list in file_sdt_ent. file_sdt_ent is allocated per file whenever a file is mapped to file_hash list. File name serves as the key to this hash table. If a file is added to this hash list, a file_sdt_ent is allocated and a list of SDT events in that file is created and assigned to sdt_list of file_sdt_ent.

Example usage :

# ./perf sdt-cache --add /home/user_app

4 events added for /home/user_app

# ./perf sdt-cache --add /lib64/libc.so.6
    8 events added for /usr/lib64/libc-2.16.so
---
 tools/perf/Documentation/perf-sdt-cache.txt |   27 +
 tools/perf/Makefile.perf                    |    4 
 tools/perf/builtin-sdt-cache.c              |   59 +++
 tools/perf/builtin.h                        |    1 
 tools/perf/command-list.txt                 |    1 
 tools/perf/perf.c                           |    1 
 tools/perf/util/parse-events.h              |    2 
 tools/perf/util/sdt.c                       |  609 +++++++++++++++++++++++++++
 8 files changed, 704 insertions(+)
 create mode 100644 tools/perf/Documentation/perf-sdt-cache.txt
 create mode 100644 tools/perf/builtin-sdt-cache.c
 create mode 100644 tools/perf/util/sdt.c
tools/perf/Documentation/perf-sdt-cache.txt new file +27 -0
new file mode 100644
index 0000000..08b9985
@@ -0,0 +1,27 @@
+perf-sdt-cache(1)
+=====================
+
+NAME
+----
+perf-sdt-cache - Manage SDT events' cache.
+
+SYNOPSIS
+--------
+[verse]
+'perf sdt-cache --add <file_name>'
+
+DESCRIPTION
+-----------
+This command manages the SDT events cache. It can add/remove SDT events
+associated with an ELF to the cache.
+
+OPTIONS
+-------
+-a::
+--add::
+        Add SDT events in the specified file to the cache. Takes file name
+	as an argument.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-report[1]
tools/perf/Makefile.perf +4 -0
index 262916f..e36f721 100644
@@ -339,6 +339,7 @@ LIB_OBJS += $(OUTPUT)util/sigchain.o
 LIB_OBJS += $(OUTPUT)util/dso.o
 LIB_OBJS += $(OUTPUT)util/symbol.o
 LIB_OBJS += $(OUTPUT)util/symbol-elf.o
+LIB_OBJS += $(OUTPUT)util/sdt.o
 LIB_OBJS += $(OUTPUT)util/color.o
 LIB_OBJS += $(OUTPUT)util/pager.o
 LIB_OBJS += $(OUTPUT)util/header.o
@@ -458,6 +459,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o
 BUILTIN_OBJS += $(OUTPUT)builtin-top.o
 BUILTIN_OBJS += $(OUTPUT)builtin-script.o
 BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
+BUILTIN_OBJS += $(OUTPUT)builtin-sdt-cache.o
 BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
 BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
 BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
@@ -485,8 +487,10 @@ LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS))
 LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS))
 LIB_OBJS := $(filter-out $(OUTPUT)util/probe-event.o,$(LIB_OBJS))
 LIB_OBJS := $(filter-out $(OUTPUT)util/probe-finder.o,$(LIB_OBJS))
+LIB_OBJS := $(filter-out $(OUTPUT)util/sdt.o,$(LIB_OBJS))
 
 BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS))
+BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-sdt-cache.o,$(BUILTIN_OBJS))
 
 # Use minimal symbol handling
 LIB_OBJS += $(OUTPUT)util/symbol-minimal.o
tools/perf/builtin-sdt-cache.c new file +59 -0

59 lines not on this page · raw message

tools/perf/builtin.h +1 -0
index b210d62..2746358 100644
@@ -37,6 +37,7 @@ extern int cmd_test(int argc, const char **argv, const char *prefix);
 extern int cmd_trace(int argc, const char **argv, const char *prefix);
 extern int cmd_inject(int argc, const char **argv, const char *prefix);
 extern int cmd_mem(int argc, const char **argv, const char *prefix);
+extern int cmd_sdt_cache(int argc, const char **argv, const char *prefix);
 
 extern int find_scripts(char **scripts_array, char **scripts_path_array);
 #endif
tools/perf/command-list.txt +1 -0
index 0906fc4..1af1f21c 100644
@@ -25,3 +25,4 @@ perf-test			mainporcelain common
 perf-timechart			mainporcelain common
 perf-top			mainporcelain common
 perf-trace			mainporcelain common
+perf-sdt-cache			mainporcelain common
tools/perf/perf.c +1 -0
index 452a847..8db763d 100644
@@ -52,6 +52,7 @@ static struct cmd_struct commands[] = {
 	{ "sched",	cmd_sched,	0 },
 #ifdef HAVE_LIBELF_SUPPORT
 	{ "probe",	cmd_probe,	0 },
+	{ "sdt-cache",  cmd_sdt_cache,  0 },
 #endif
 	{ "kmem",	cmd_kmem,	0 },
 	{ "lock",	cmd_lock,	0 },
tools/perf/util/parse-events.h +2 -0
index df094b4..e6efe2c 100644
@@ -109,4 +109,6 @@ extern int is_valid_tracepoint(const char *event_string);
 
 extern int valid_debugfs_mount(const char *debugfs);
 
+int add_sdt_events(const char *file);
+
 #endif /* __PERF_PARSE_EVENTS_H */
tools/perf/util/sdt.c new file +609 -0

609 lines not on this page · raw message

maintainer
#5 lore raw

Re: [PATCH v3 2/5] perf/sdt: Add SDT events into a cache

Quoted 39 lines · Hemant Kumar

This patch adds a new sub-command to perf : sdt-cache. sdt-cache command can be used to add SDT events.
When user invokes "perf sdt-cache add <file-name>", a hash table/list is created named as file_hash list. A typical entry in a file_hash table looks like:

                file hash      file_sdt_ent     file_sdt_ent
                |---------|   ---------------   -------------
                | hlist ==|===|=>file_list =|===|=>file_list=|==..
    key = 644 =>|         |   | sbuild_id   |   | sbuild_id  |
                |---------|   | name        |   | name       |
                |         |   | sdt_list    |   | sdt_list   |
    key = 645 =>| hlist   |   |   ||        |   |    ||      |
                |---------|   ---------------   --------------

|| || || Connected to SDT notes
---------------
| note_list |
| name |sdt_note
| provider |
-----||--------
connected to other SDT notes

Each entry of the file_hash table is an hlist which connects to file_list in file_sdt_ent. file_sdt_ent is allocated per file whenever a file is mapped to file_hash list. File name serves as the key to this hash table. If a file is added to this hash list, a file_sdt_ent is allocated and a list of SDT events in that file is created and assigned to sdt_list of file_sdt_ent.

Example usage :

# ./perf sdt-cache --add /home/user_app

4 events added for /home/user_app

# ./perf sdt-cache --add /lib64/libc.so.6
    8 events added for /usr/lib64/libc-2.16.so

[SNIP]

Quoted 17 lines
tools/perf/util/sdt.c new file +17 -0
+static int file_hash_list__remove(struct hash_table *file_hash,
+				   const char *target)
+{
+	struct file_sdt_ent *fse;
+	struct hlist_head *ent_head;
+	struct list_head *sdt_head;
+	struct hlist_node *tmp1;
+	char *res_path;
+	int key, nr_del = 0;
+
+	res_path = realpath(target, NULL);
+	if (!res_path)
+		return -ENOMEM;
+
+	key = get_hash_key(target);
+	ent_head = &file_hash->ent[key];
+

tools/perf/util/sdt.c line 195

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+	/* Got the file hash entry */
+	hlist_for_each_entry_safe(fse, tmp1, ent_head, file_list) {
+		sdt_head = &fse->sdt_list;
+		if (strcmp(res_path, fse->name))
+			continue;
+
+		/* Got the file list entry, now start removing */
+		nr_del = cleanup_sdt_note_list(sdt_head);
+		hlist_del(&fse->file_list);
+		list_del(&fse->sdt_list);

It seems you don't need to call list_del() here. And what about just calling cleanup_sdt_note_list(&fse->sdt_list) instead?

Quoted 5 lines
tools/perf/util/sdt.c new file +5 -0
+		free(fse);
+	}
+	free(res_path);
+	return nr_del;
+}

[SNIP]

tools/perf/util/sdt.c line 243

Quoted 8 lines
tools/perf/util/sdt.c new file +8 -0
+static int sdt_note__read(char *data, struct list_head *sdt_list)
+{
+	struct sdt_note *sn;
+	char *tmp, *ptr, delim[2];
+	int ret = -EBADF, val;
+
+	/* Initialize the delimiter with DELIM */
+	delim[0] = DELIM;

What about delim[1] then?

Quoted 46 lines
tools/perf/util/sdt.c new file +46 -0
+	sn = malloc(sizeof(*sn));
+	if (!sn) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	INIT_LIST_HEAD(&sn->note_list);
+
+	/* Provider name */
+	ptr = strtok_r(data, delim, &tmp);
+	if (!ptr)
+		goto out;
+	sn->provider = strdup(ptr);
+	if (!sn->provider) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* Marker name */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	sn->name = strdup(ptr);
+	if (!sn->name) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* Location */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	val = sscanf(ptr, "%lx", &sn->addr.a64[0]);
+	if (val == EOF)
+		goto out;
+	/* Sempahore */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	val = sscanf(ptr, "%lx", &sn->addr.a64[2]);
+	if (val == EOF)
+		goto out;
+	/* Add to the SDT list */
+	list_add(&sn->note_list, sdt_list);
+	ret = 0;
+out:
+	return ret;
+}
+

tools/perf/util/sdt.c line 299

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+/**
+ * file_hash_list__populate: Fill up the file hash table
+ * @file_hash: empty file hash table
+ * @cache: FILE * to read from
+ *
+ * Parse @cache for file_name and its SDT events.
+ * The format of the cache is :
+ *
+ * file_name:build_id\n
+ * provider:marker:location:semaphore\n

You seem forgot to add a file delimiter before new file.

      :\n
Quoted 2 lines

+ * file_name:build_id\n
+ * ...

Anyway IMHO it's bit uncomfortable. How about changing the format same as it dumps? A line starts with '%' is a sdt description, otherwise file info:

file_name1:build_id\n
%provide:marker1:location:semaphore\n
%provide:marker2:location:semaphore\n
%...
file_name2:build_id\n
%...

And we can deal with it by usual fgets/getline and strtok.

Oh, it's just a suggestion..

Quoted 43 lines

+ *
+ * Parse according to the above format. Find out the file_name and build_id
+ * first and then use sdt_note__read() to parse the SDT note info.
+ * Find out the hash key from the file_name and use that to add this new
+ * entry to file hash.
+ */

tools/perf/util/sdt.c new file +37 -0
+static int file_hash_list__populate(struct hash_table *file_hash, FILE *cache)
+{
+	struct file_sdt_ent *fse;
+	int key, val, ret = -EBADF;
+	char data[2 * PATH_MAX], *ptr, *tmp;
+
+	for (val = fscanf(cache, "%s", data); val != EOF;
+	     val = fscanf(cache, "%s", data)) {
+		fse = malloc(sizeof(*fse));
+		if (!fse) {
+			ret = -ENOMEM;
+			break;
+		}
+		INIT_LIST_HEAD(&fse->sdt_list);
+		INIT_HLIST_NODE(&fse->file_list);
+
+		/* First line is file name and build id */
+		ptr = strtok_r(data, ":", &tmp);
+		if (!ptr)
+			break;
+		strcpy(fse->name, ptr);
+		ptr = strtok_r(NULL, ":", &tmp);
+		if (!ptr)
+			break;
+		strcpy(fse->sbuild_id, ptr);
+
+		/* Now, try to get the SDT notes for that file */
+		while ((fscanf(cache, "%s", data)) != EOF) {
+			if (!strcmp(data, ":"))
+				break;
+			ret = sdt_note__read(data, &fse->sdt_list);
+			if (ret < 0)
+				goto out;
+		}
+		key = get_hash_key(fse->name);
+		hlist_add_head(&fse->file_list, &file_hash->ent[key]);
+		ret = 0;

tools/perf/util/sdt.c line 354

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+	}
+out:
+	return ret;
+}
+
+/**
+ * file_hash_list__init: Initializes the file hash list
+ * @file_hash: empty file_hash
+ *
+ * Initializes the ent's of file_hash and opens the cache file.

What is the ent's?

tools/perf/util/sdt.c line 361

Quoted 7 lines

+ * To look for the cache file, look into the directory in HOME env variable.
+ */

tools/perf/util/sdt.c new file +5 -0
+static int file_hash_list__init(struct hash_table *file_hash)
+{
+	FILE *cache;
+	int i, ret = 0;
+	char sdt_cache_path[MAXPATHLEN], *val;

s/MAXPATHLEN/PATH_MAX/ ?

tools/perf/util/sdt.c line 369

Quoted 8 lines
tools/perf/util/sdt.c new file +8 -0
+
+	for (i = 0; i < SDT_HASH_SIZE; i++)
+		INIT_HLIST_HEAD(&file_hash->ent[i]);
+
+	val = getenv("HOME");
+	if (val)
+		snprintf(sdt_cache_path, MAXPATHLEN-1, "%s/%s/%s",
+			 val, SDT_CACHE_DIR, SDT_FILE_CACHE);

Then you can use scnprintf(sdt_cache_path, sizeof(sdt_cache_path), ...).

Quoted 30 lines
tools/perf/util/sdt.c new file +30 -0
+	else {
+		pr_err("Error: Couldn't get user's home directory\n");
+		ret = -1;
+		goto out;
+	}
+
+	cache = fopen(sdt_cache_path, "r");
+	if (cache == NULL)
+		goto out;
+
+	/* Populate the hash list */
+	ret = file_hash_list__populate(file_hash, cache);
+	fclose(cache);
+out:
+	return ret;
+}
+
+/**
+ * file_hash_list__cleanup: Frees up all the space taken by file_hash list
+ * @file_hash: file_hash table
+ */
+static void file_hash_list__cleanup(struct hash_table *file_hash)
+{
+	struct file_sdt_ent *file_pos;
+	struct list_head *sdt_head;
+	struct hlist_head *ent_head;
+	struct hlist_node *tmp;
+	int i;
+
+	/* Go through all the entries */

tools/perf/util/sdt.c line 409

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+	for (i = 0; i < SDT_HASH_SIZE; i++) {
+		ent_head = &file_hash->ent[i];
+
+		hlist_for_each_entry_safe(file_pos, tmp, ent_head, file_list) {
+			sdt_head = &file_pos->sdt_list;
+
+			/* Cleanup the corresponding SDT notes' list */
+			cleanup_sdt_note_list(sdt_head);
+			hlist_del(&file_pos->file_list);
+			list_del(&file_pos->sdt_list);

Ditto. Don't call list_del() but call cleanup_sdt_note_list directly.

Quoted 26 lines
tools/perf/util/sdt.c new file +26 -0
+			free(file_pos);
+		}
+	}
+}
+
+
+/**
+ * add_to_hash_list: add an entry to file_hash_list
+ * @file_hash: file hash table
+ * @target: file name
+ *
+ * Finds out the build_id of @target, checks if @target is already present
+ * in file hash list. If not present, delete any stale entries with this
+ * file name (i.e., entries matching this file name but having older
+ * build ids). And then, adds the file entry to file hash list and also
+ * updates the SDT events in the event hash list.
+ */
+static int add_to_hash_list(struct hash_table *file_hash, const char *target)
+{
+	struct file_info *file;
+	int ret = -EBADF;
+	u8 build_id[BUILD_ID_SIZE];
+
+	LIST_HEAD(sdt_notes);
+
+	file = malloc(sizeof(*file));

tools/perf/util/sdt.c line 445

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+	if (!file)
+		return -ENOMEM;
+
+	file->name = realpath(target, NULL);
+	if (!file->name) {
+		pr_err("%s: Bad file name\n", target);
+		goto out;
+	}
+
+	symbol__elf_init();

No need to call symbol__elf_init() here since we already called it in cmd_sdt_cache().

Quoted 33 lines
tools/perf/util/sdt.c new file +33 -0
+	if (filename__read_build_id(file->name, &build_id,
+				    sizeof(build_id)) < 0) {
+		pr_err("Couldn't read build-id in %s\n", file->name);
+		sdt_err(ret, file->name);
+		goto out;
+	}
+	build_id__sprintf(build_id, sizeof(build_id), file->sbuild_id);
+
+	/* File entry already exists ?*/
+	if (file_hash_list__entry_exists(file_hash, file)) {
+		pr_err("Error: SDT events for %s already exist\n",
+		       file->name);
+		ret = 0;
+		goto out;
+	}
+
+	/*
+	 * This should remove any stale entries (if any) from the file hash.
+	 * Stale entries will have the same file name but different build ids.
+	 */
+	ret = file_hash_list__remove(file_hash, file->name);
+	if (ret < 0)
+		goto out;
+	ret = get_sdt_note_list(&sdt_notes, file->name);
+	if (ret < 0)
+		sdt_err(ret, target);
+	/* Add the entry to file hash list */
+	ret = file_hash_list__add(file_hash, &sdt_notes, file);
+out:
+	free(file->name);
+	free(file);
+	return ret;
+}

[SNIP]

tools/perf/util/sdt.c line 542

Quoted 12 lines
tools/perf/util/sdt.c new file +12 -0
+static int flush_hash_list_to_cache(struct hash_table *file_hash)
+{
+	FILE *cache;
+	int ret;
+	struct stat buf;
+	char sdt_cache_path[MAXPATHLEN], sdt_dir[MAXPATHLEN], *val;
+
+	val = getenv("HOME");
+	if (val) {
+		snprintf(sdt_dir, MAXPATHLEN-1, "%s/%s", val, SDT_CACHE_DIR);
+		snprintf(sdt_cache_path, MAXPATHLEN-1, "%s/%s",
+			 sdt_dir, SDT_FILE_CACHE);

Ditto. s/MAXPATHLEN/PATH_MAX/g and use scnprintf().

Thanks,
Namhyung

Quoted 26 lines
tools/perf/util/sdt.c new file +26 -0
+	} else {
+		pr_err("Error: Couldn't get the user's home directory\n");
+		ret = -1;
+		goto out_err;
+	}
+	ret = stat(sdt_dir, &buf);
+	if (ret) {
+		ret = mkdir(sdt_dir, buf.st_mode);
+		if (ret) {
+			pr_err("Error: Couldn't create %s\n", sdt_dir);
+			goto out_err;
+		}
+	}
+
+	cache = fopen(sdt_cache_path, "w");
+	if (!cache) {
+		pr_err("Error in creating %s file\n", sdt_cache_path);
+		ret = -errno;
+		goto out_err;
+	}
+
+	file_hash_list__flush(file_hash, cache);
+	fclose(cache);
+out_err:
+	return ret;
+}
#6 lore raw

Re: [PATCH v3 2/5] perf/sdt: Add SDT events into a cache

Quoted 62 lines · Namhyung Kim
Quoted 39 lines · Hemant Kumar

This patch adds a new sub-command to perf : sdt-cache. sdt-cache command can be used to add SDT events.
When user invokes "perf sdt-cache add <file-name>", a hash table/list is created named as file_hash list. A typical entry in a file_hash table looks like:

                 file hash      file_sdt_ent     file_sdt_ent
                 |---------|   ---------------   -------------
                 | hlist ==|===|=>file_list =|===|=>file_list=|==..
     key = 644 =>|         |   | sbuild_id   |   | sbuild_id  |
                 |---------|   | name        |   | name       |
                 |         |   | sdt_list    |   | sdt_list   |
     key = 645 =>| hlist   |   |   ||        |   |    ||      |
                 |---------|   ---------------   --------------

|| || || Connected to SDT notes
---------------
| note_list |
| name |sdt_note
| provider |
-----||--------
connected to other SDT notes

Each entry of the file_hash table is an hlist which connects to file_list in file_sdt_ent. file_sdt_ent is allocated per file whenever a file is mapped to file_hash list. File name serves as the key to this hash table. If a file is added to this hash list, a file_sdt_ent is allocated and a list of SDT events in that file is created and assigned to sdt_list of file_sdt_ent.

Example usage :

# ./perf sdt-cache --add /home/user_app

4 events added for /home/user_app

# ./perf sdt-cache --add /lib64/libc.so.6
     8 events added for /usr/lib64/libc-2.16.so

[SNIP]

Quoted 21 lines
tools/perf/util/sdt.c new file +21 -0
+static int file_hash_list__remove(struct hash_table *file_hash,
+				   const char *target)
+{
+	struct file_sdt_ent *fse;
+	struct hlist_head *ent_head;
+	struct list_head *sdt_head;
+	struct hlist_node *tmp1;
+	char *res_path;
+	int key, nr_del = 0;
+
+	res_path = realpath(target, NULL);
+	if (!res_path)
+		return -ENOMEM;
+
+	key = get_hash_key(target);
+	ent_head = &file_hash->ent[key];
+
+	/* Got the file hash entry */
+	hlist_for_each_entry_safe(fse, tmp1, ent_head, file_list) {
+		sdt_head = &fse->sdt_list;
+		if (strcmp(res_path, fse->name))
Quoted 8 lines

tools/perf/util/sdt.c line 195

Quoted 6 lines
tools/perf/util/sdt.c new file +6 -0
+			continue;
+
+		/* Got the file list entry, now start removing */
+		nr_del = cleanup_sdt_note_list(sdt_head);
+		hlist_del(&fse->file_list);
+		list_del(&fse->sdt_list);

It seems you don't need to call list_del() here. And what about just calling cleanup_sdt_note_list(&fse->sdt_list) instead?

Yeah, will remove this.

Quoted 7 lines
Quoted 6 lines
tools/perf/util/sdt.c new file +5 -0
+		free(fse);
+	}
+	free(res_path);
+	return nr_del;
+}

[SNIP]

Quoted 10 lines

tools/perf/util/sdt.c line 243

Quoted 8 lines
tools/perf/util/sdt.c new file +8 -0
+static int sdt_note__read(char *data, struct list_head *sdt_list)
+{
+	struct sdt_note *sn;
+	char *tmp, *ptr, delim[2];
+	int ret = -EBADF, val;
+
+	/* Initialize the delimiter with DELIM */
+	delim[0] = DELIM;

What about delim[1] then?

It should be '\0' here, will initialize it.

Quoted 72 lines
Quoted 46 lines
tools/perf/util/sdt.c new file +46 -0
+	sn = malloc(sizeof(*sn));
+	if (!sn) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	INIT_LIST_HEAD(&sn->note_list);
+
+	/* Provider name */
+	ptr = strtok_r(data, delim, &tmp);
+	if (!ptr)
+		goto out;
+	sn->provider = strdup(ptr);
+	if (!sn->provider) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* Marker name */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	sn->name = strdup(ptr);
+	if (!sn->name) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* Location */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	val = sscanf(ptr, "%lx", &sn->addr.a64[0]);
+	if (val == EOF)
+		goto out;
+	/* Sempahore */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	val = sscanf(ptr, "%lx", &sn->addr.a64[2]);
+	if (val == EOF)
+		goto out;
+	/* Add to the SDT list */
+	list_add(&sn->note_list, sdt_list);
+	ret = 0;
+out:
+	return ret;
+}
+

tools/perf/util/sdt.c line 299

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+/**
+ * file_hash_list__populate: Fill up the file hash table
+ * @file_hash: empty file hash table
+ * @cache: FILE * to read from
+ *
+ * Parse @cache for file_name and its SDT events.
+ * The format of the cache is :
+ *
+ * file_name:build_id\n
+ * provider:marker:location:semaphore\n

You seem forgot to add a file delimiter before new file.

       :\n
Quoted 2 lines

+ * file_name:build_id\n
+ * ...

Anyway IMHO it's bit uncomfortable. How about changing the format same as it dumps? A line starts with '%' is a sdt description, otherwise file info:

file_name1:build_id\n
%provide:marker1:location:semaphore\n
%provide:marker2:location:semaphore\n
%...
file_name2:build_id\n
%...

Quoted 3 lines

And we can deal with it by usual fgets/getline and strtok.

Oh, it's just a suggestion..

No problem. Will change the format to above.

Quoted 47 lines
Quoted 47 lines

+ *
+ * Parse according to the above format. Find out the file_name and build_id
+ * first and then use sdt_note__read() to parse the SDT note info.
+ * Find out the hash key from the file_name and use that to add this new
+ * entry to file hash.
+ */

tools/perf/util/sdt.c new file +40 -0
+static int file_hash_list__populate(struct hash_table *file_hash, FILE *cache)
+{
+	struct file_sdt_ent *fse;
+	int key, val, ret = -EBADF;
+	char data[2 * PATH_MAX], *ptr, *tmp;
+
+	for (val = fscanf(cache, "%s", data); val != EOF;
+	     val = fscanf(cache, "%s", data)) {
+		fse = malloc(sizeof(*fse));
+		if (!fse) {
+			ret = -ENOMEM;
+			break;
+		}
+		INIT_LIST_HEAD(&fse->sdt_list);
+		INIT_HLIST_NODE(&fse->file_list);
+
+		/* First line is file name and build id */
+		ptr = strtok_r(data, ":", &tmp);
+		if (!ptr)
+			break;
+		strcpy(fse->name, ptr);
+		ptr = strtok_r(NULL, ":", &tmp);
+		if (!ptr)
+			break;
+		strcpy(fse->sbuild_id, ptr);
+
+		/* Now, try to get the SDT notes for that file */
+		while ((fscanf(cache, "%s", data)) != EOF) {
+			if (!strcmp(data, ":"))
+				break;
+			ret = sdt_note__read(data, &fse->sdt_list);
+			if (ret < 0)
+				goto out;
+		}
+		key = get_hash_key(fse->name);
+		hlist_add_head(&fse->file_list, &file_hash->ent[key]);
+		ret = 0;
+	}
+out:
+	return ret;
Quoted 8 lines

tools/perf/util/sdt.c line 354

Quoted 7 lines
tools/perf/util/sdt.c new file +7 -0
+}
+
+/**
+ * file_hash_list__init: Initializes the file hash list
+ * @file_hash: empty file_hash
+ *
+ * Initializes the ent's of file_hash and opens the cache file.

What is the ent's?

Hash entries. :) Will change to that to entries.

Quoted 8 lines

tools/perf/util/sdt.c line 361

Quoted 7 lines

+ * To look for the cache file, look into the directory in HOME env variable.
+ */

tools/perf/util/sdt.c new file +5 -0
+static int file_hash_list__init(struct hash_table *file_hash)
+{
+	FILE *cache;
+	int i, ret = 0;
+	char sdt_cache_path[MAXPATHLEN], *val;

s/MAXPATHLEN/PATH_MAX/ ?

Ok, for consistency, will change it to PATH_MAX.

Quoted 9 lines

tools/perf/util/sdt.c line 369

Quoted 8 lines
tools/perf/util/sdt.c new file +8 -0
+
+	for (i = 0; i < SDT_HASH_SIZE; i++)
+		INIT_HLIST_HEAD(&file_hash->ent[i]);
+
+	val = getenv("HOME");
+	if (val)
+		snprintf(sdt_cache_path, MAXPATHLEN-1, "%s/%s/%s",
+			 val, SDT_CACHE_DIR, SDT_FILE_CACHE);

Then you can use scnprintf(sdt_cache_path, sizeof(sdt_cache_path), ...).

Ok.

Quoted 34 lines
Quoted 34 lines
tools/perf/util/sdt.c new file +33 -0
+	else {
+		pr_err("Error: Couldn't get user's home directory\n");
+		ret = -1;
+		goto out;
+	}
+
+	cache = fopen(sdt_cache_path, "r");
+	if (cache == NULL)
+		goto out;
+
+	/* Populate the hash list */
+	ret = file_hash_list__populate(file_hash, cache);
+	fclose(cache);
+out:
+	return ret;
+}
+
+/**
+ * file_hash_list__cleanup: Frees up all the space taken by file_hash list
+ * @file_hash: file_hash table
+ */
+static void file_hash_list__cleanup(struct hash_table *file_hash)
+{
+	struct file_sdt_ent *file_pos;
+	struct list_head *sdt_head;
+	struct hlist_head *ent_head;
+	struct hlist_node *tmp;
+	int i;
+
+	/* Go through all the entries */
+	for (i = 0; i < SDT_HASH_SIZE; i++) {
+		ent_head = &file_hash->ent[i];
+
Quoted 9 lines

tools/perf/util/sdt.c line 409

Quoted 7 lines
tools/perf/util/sdt.c new file +7 -0
+		hlist_for_each_entry_safe(file_pos, tmp, ent_head, file_list) {
+			sdt_head = &file_pos->sdt_list;
+
+			/* Cleanup the corresponding SDT notes' list */
+			cleanup_sdt_note_list(sdt_head);
+			hlist_del(&file_pos->file_list);
+			list_del(&file_pos->sdt_list);

Ditto. Don't call list_del() but call cleanup_sdt_note_list directly.

Sure.

Quoted 30 lines
Quoted 30 lines
tools/perf/util/sdt.c new file +30 -0
+			free(file_pos);
+		}
+	}
+}
+
+
+/**
+ * add_to_hash_list: add an entry to file_hash_list
+ * @file_hash: file hash table
+ * @target: file name
+ *
+ * Finds out the build_id of @target, checks if @target is already present
+ * in file hash list. If not present, delete any stale entries with this
+ * file name (i.e., entries matching this file name but having older
+ * build ids). And then, adds the file entry to file hash list and also
+ * updates the SDT events in the event hash list.
+ */
+static int add_to_hash_list(struct hash_table *file_hash, const char *target)
+{
+	struct file_info *file;
+	int ret = -EBADF;
+	u8 build_id[BUILD_ID_SIZE];
+
+	LIST_HEAD(sdt_notes);
+
+	file = malloc(sizeof(*file));
+	if (!file)
+		return -ENOMEM;
+
+	file->name = realpath(target, NULL);
Quoted 8 lines

tools/perf/util/sdt.c line 445

Quoted 6 lines
tools/perf/util/sdt.c new file +6 -0
+	if (!file->name) {
+		pr_err("%s: Bad file name\n", target);
+		goto out;
+	}
+
+	symbol__elf_init();

No need to call symbol__elf_init() here since we already called it in cmd_sdt_cache().

Yeah, my bad. Will remove it.

Quoted 71 lines
Quoted 34 lines
tools/perf/util/sdt.c new file +33 -0
+	if (filename__read_build_id(file->name, &build_id,
+				    sizeof(build_id)) < 0) {
+		pr_err("Couldn't read build-id in %s\n", file->name);
+		sdt_err(ret, file->name);
+		goto out;
+	}
+	build_id__sprintf(build_id, sizeof(build_id), file->sbuild_id);
+
+	/* File entry already exists ?*/
+	if (file_hash_list__entry_exists(file_hash, file)) {
+		pr_err("Error: SDT events for %s already exist\n",
+		       file->name);
+		ret = 0;
+		goto out;
+	}
+
+	/*
+	 * This should remove any stale entries (if any) from the file hash.
+	 * Stale entries will have the same file name but different build ids.
+	 */
+	ret = file_hash_list__remove(file_hash, file->name);
+	if (ret < 0)
+		goto out;
+	ret = get_sdt_note_list(&sdt_notes, file->name);
+	if (ret < 0)
+		sdt_err(ret, target);
+	/* Add the entry to file hash list */
+	ret = file_hash_list__add(file_hash, &sdt_notes, file);
+out:
+	free(file->name);
+	free(file);
+	return ret;
+}

[SNIP]

tools/perf/util/sdt.c line 542

Quoted 12 lines
tools/perf/util/sdt.c new file +12 -0
+static int flush_hash_list_to_cache(struct hash_table *file_hash)
+{
+	FILE *cache;
+	int ret;
+	struct stat buf;
+	char sdt_cache_path[MAXPATHLEN], sdt_dir[MAXPATHLEN], *val;
+
+	val = getenv("HOME");
+	if (val) {
+		snprintf(sdt_dir, MAXPATHLEN-1, "%s/%s", val, SDT_CACHE_DIR);
+		snprintf(sdt_cache_path, MAXPATHLEN-1, "%s/%s",
+			 sdt_dir, SDT_FILE_CACHE);

Ditto. s/MAXPATHLEN/PATH_MAX/g and use scnprintf().

Thanks,
Namhyung

Quoted 18 lines
tools/perf/util/sdt.c new file +18 -0
+	} else {
+		pr_err("Error: Couldn't get the user's home directory\n");
+		ret = -1;
+		goto out_err;
+	}
+	ret = stat(sdt_dir, &buf);
+	if (ret) {
+		ret = mkdir(sdt_dir, buf.st_mode);
+		if (ret) {
+			pr_err("Error: Couldn't create %s\n", sdt_dir);
+			goto out_err;
+		}
+	}
+
+	cache = fopen(sdt_cache_path, "w");
+	if (!cache) {
+		pr_err("Error in creating %s file\n", sdt_cache_path);
+		ret = -errno;
Quoted 8 lines

tools/perf/util/sdt.c line 568

Quoted 8 lines
tools/perf/util/sdt.c new file +8 -0
+		goto out_err;
+	}
+
+	file_hash_list__flush(file_hash, cache);
+	fclose(cache);
+out_err:
+	return ret;
+}

Thanks a lot for reviewing the patch.

Signature · 2 lines
Thanks,
Hemant Kumar
#7 lore raw

Re: [PATCH v3 2/5] perf/sdt: Add SDT events into a cache

In reply to Hemant Kumar

Hi Hermant,

tools/perf/util/sdt.c line 236

Quoted 10 lines · Hemant Kumar
tools/perf/util/sdt.c new file +10 -0
+
+/**
+ * sdt_note__read: Parse SDT note info
+ * @data: string containing the SDT note's info
+ * @sdt_list: empty list
+ *
+ * Parse @data to find out SDT note name, provider, location and semaphore.
+ * All these data are separated by DELIM.
+ */
+static int sdt_note__read(char *data, struct list_head *sdt_list)

It seems that you can simply use sscanf() instead of this code, like this.

n = sscanf(data, "%ms:%ms:%lx:%lx", &sn->provider, &sn->name,

           &sn->addr.a64[0], &sn->addr.a64[2]);
if (n != 4)
	goto error;
Quoted 69 lines
tools/perf/util/sdt.c new file +69 -0
+{
+	struct sdt_note *sn;
+	char *tmp, *ptr, delim[2];
+	int ret = -EBADF, val;
+
+	/* Initialize the delimiter with DELIM */
+	delim[0] = DELIM;
+	sn = malloc(sizeof(*sn));
+	if (!sn) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	INIT_LIST_HEAD(&sn->note_list);
+
+	/* Provider name */
+	ptr = strtok_r(data, delim, &tmp);
+	if (!ptr)
+		goto out;
+	sn->provider = strdup(ptr);
+	if (!sn->provider) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* Marker name */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	sn->name = strdup(ptr);
+	if (!sn->name) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* Location */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	val = sscanf(ptr, "%lx", &sn->addr.a64[0]);
+	if (val == EOF)
+		goto out;
+	/* Sempahore */
+	ptr = strtok_r(NULL, delim, &tmp);
+	if (!ptr)
+		goto out;
+	val = sscanf(ptr, "%lx", &sn->addr.a64[2]);
+	if (val == EOF)
+		goto out;
+	/* Add to the SDT list */
+	list_add(&sn->note_list, sdt_list);
+	ret = 0;
+out:
+	return ret;
+}
+
+/**
+ * file_hash_list__populate: Fill up the file hash table
+ * @file_hash: empty file hash table
+ * @cache: FILE * to read from
+ *
+ * Parse @cache for file_name and its SDT events.
+ * The format of the cache is :
+ *
+ * file_name:build_id\n
+ * provider:marker:location:semaphore\n
+ * file_name:build_id\n
+ * ...
+ *
+ * Parse according to the above format. Find out the file_name and build_id
+ * first and then use sdt_note__read() to parse the SDT note info.
+ * Find out the hash key from the file_name and use that to add this new

tools/perf/util/sdt.c line 315

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+ * entry to file hash.
+ */
+static int file_hash_list__populate(struct hash_table *file_hash, FILE *cache)
+{
+	struct file_sdt_ent *fse;
+	int key, val, ret = -EBADF;
+	char data[2 * PATH_MAX], *ptr, *tmp;
+
+	for (val = fscanf(cache, "%s", data); val != EOF;
+	     val = fscanf(cache, "%s", data)) {

And no, to read one-line, please use fgets instead of fscanf.

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+		fse = malloc(sizeof(*fse));
+		if (!fse) {
+			ret = -ENOMEM;
+			break;
+		}
+		INIT_LIST_HEAD(&fse->sdt_list);
+		INIT_HLIST_NODE(&fse->file_list);
+
+		/* First line is file name and build id */
+		ptr = strtok_r(data, ":", &tmp);

tools/perf/util/sdt.c line 335

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+		if (!ptr)
+			break;
+		strcpy(fse->name, ptr);
+		ptr = strtok_r(NULL, ":", &tmp);
+		if (!ptr)
+			break;
+		strcpy(fse->sbuild_id, ptr);
+
+		/* Now, try to get the SDT notes for that file */
+		while ((fscanf(cache, "%s", data)) != EOF) {

Ditto.

Quoted 163 lines
tools/perf/util/sdt.c new file +163 -0
+			if (!strcmp(data, ":"))
+				break;
+			ret = sdt_note__read(data, &fse->sdt_list);
+			if (ret < 0)
+				goto out;
+		}
+		key = get_hash_key(fse->name);
+		hlist_add_head(&fse->file_list, &file_hash->ent[key]);
+		ret = 0;
+	}
+out:
+	return ret;
+}
+
+/**
+ * file_hash_list__init: Initializes the file hash list
+ * @file_hash: empty file_hash
+ *
+ * Initializes the ent's of file_hash and opens the cache file.
+ * To look for the cache file, look into the directory in HOME env variable.
+ */
+static int file_hash_list__init(struct hash_table *file_hash)
+{
+	FILE *cache;
+	int i, ret = 0;
+	char sdt_cache_path[MAXPATHLEN], *val;
+
+	for (i = 0; i < SDT_HASH_SIZE; i++)
+		INIT_HLIST_HEAD(&file_hash->ent[i]);
+
+	val = getenv("HOME");
+	if (val)
+		snprintf(sdt_cache_path, MAXPATHLEN-1, "%s/%s/%s",
+			 val, SDT_CACHE_DIR, SDT_FILE_CACHE);
+	else {
+		pr_err("Error: Couldn't get user's home directory\n");
+		ret = -1;
+		goto out;
+	}
+
+	cache = fopen(sdt_cache_path, "r");
+	if (cache == NULL)
+		goto out;
+
+	/* Populate the hash list */
+	ret = file_hash_list__populate(file_hash, cache);
+	fclose(cache);
+out:
+	return ret;
+}
+
+/**
+ * file_hash_list__cleanup: Frees up all the space taken by file_hash list
+ * @file_hash: file_hash table
+ */
+static void file_hash_list__cleanup(struct hash_table *file_hash)
+{
+	struct file_sdt_ent *file_pos;
+	struct list_head *sdt_head;
+	struct hlist_head *ent_head;
+	struct hlist_node *tmp;
+	int i;
+
+	/* Go through all the entries */
+	for (i = 0; i < SDT_HASH_SIZE; i++) {
+		ent_head = &file_hash->ent[i];
+
+		hlist_for_each_entry_safe(file_pos, tmp, ent_head, file_list) {
+			sdt_head = &file_pos->sdt_list;
+
+			/* Cleanup the corresponding SDT notes' list */
+			cleanup_sdt_note_list(sdt_head);
+			hlist_del(&file_pos->file_list);
+			list_del(&file_pos->sdt_list);
+			free(file_pos);
+		}
+	}
+}
+
+
+/**
+ * add_to_hash_list: add an entry to file_hash_list
+ * @file_hash: file hash table
+ * @target: file name
+ *
+ * Finds out the build_id of @target, checks if @target is already present
+ * in file hash list. If not present, delete any stale entries with this
+ * file name (i.e., entries matching this file name but having older
+ * build ids). And then, adds the file entry to file hash list and also
+ * updates the SDT events in the event hash list.
+ */
+static int add_to_hash_list(struct hash_table *file_hash, const char *target)
+{
+	struct file_info *file;
+	int ret = -EBADF;
+	u8 build_id[BUILD_ID_SIZE];
+
+	LIST_HEAD(sdt_notes);
+
+	file = malloc(sizeof(*file));
+	if (!file)
+		return -ENOMEM;
+
+	file->name = realpath(target, NULL);
+	if (!file->name) {
+		pr_err("%s: Bad file name\n", target);
+		goto out;
+	}
+
+	symbol__elf_init();
+	if (filename__read_build_id(file->name, &build_id,
+				    sizeof(build_id)) < 0) {
+		pr_err("Couldn't read build-id in %s\n", file->name);
+		sdt_err(ret, file->name);
+		goto out;
+	}
+	build_id__sprintf(build_id, sizeof(build_id), file->sbuild_id);
+
+	/* File entry already exists ?*/
+	if (file_hash_list__entry_exists(file_hash, file)) {
+		pr_err("Error: SDT events for %s already exist\n",
+		       file->name);
+		ret = 0;
+		goto out;
+	}
+
+	/*
+	 * This should remove any stale entries (if any) from the file hash.
+	 * Stale entries will have the same file name but different build ids.
+	 */
+	ret = file_hash_list__remove(file_hash, file->name);
+	if (ret < 0)
+		goto out;
+	ret = get_sdt_note_list(&sdt_notes, file->name);
+	if (ret < 0)
+		sdt_err(ret, target);
+	/* Add the entry to file hash list */
+	ret = file_hash_list__add(file_hash, &sdt_notes, file);
+out:
+	free(file->name);
+	free(file);
+	return ret;
+}
+
+/**
+ * file_hash_list__flush: Flush the file_hash list to cache
+ * @file_hash: file_hash list
+ * @fcache: opened SDT events cache
+ *
+ * Iterate through all the entries of @file_hash and flush them
+ * onto fcache.
+ * The complete file hash list is flushed into the cache. Write the
+ * file entries for every ent of file_hash, alongwith the SDT notes. The
+ * delimiter used is DELIM.
+ */
+static void file_hash_list__flush(struct hash_table *file_hash,
+				  FILE *fcache)
+{
+	struct file_sdt_ent *file_pos;
+	struct list_head *sdt_head;
+	struct hlist_head *ent_head;
+	struct sdt_note *sdt_ptr;
+	int i;

tools/perf/util/sdt.c line 508

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+
+	/* Go through all entries */
+	for (i = 0; i < SDT_HASH_SIZE; i++) {
+		/* Obtain the list head */
+		ent_head = &file_hash->ent[i];
+
+		/* DELIM is used here as delimiter */
+		hlist_for_each_entry(file_pos, ent_head, file_list) {
+			fprintf(fcache, "%s%c", file_pos->name, DELIM);
+			fprintf(fcache, "%s\n", file_pos->sbuild_id);

Here, you also should merge these 2 sequential fprintfs.

Quoted 11 lines
tools/perf/util/sdt.c new file +11 -0
+			sdt_head = &file_pos->sdt_list;
+			list_for_each_entry(sdt_ptr, sdt_head, note_list) {
+				fprintf(fcache, "%s%c%s%c%lx%c%lx\n",
+					sdt_ptr->provider, DELIM,
+					sdt_ptr->name, DELIM,
+					sdt_ptr->addr.a64[0], DELIM,
+					sdt_ptr->addr.a64[2]);
+			}
+			/* This marks the end of the SDT notes for a file */
+			fprintf(fcache, "%c\n", DELIM);
+		}

tools/perf/util/sdt.c line 529

Quoted 10 lines
tools/perf/util/sdt.c new file +10 -0
+	}
+}
+
+/**
+ * flush_hash_list_to_cache: Flush everything from file_hash to disk
+ * @file_hash: file_hash list
+ *
+ * Opens a cache and calls file_hash_list__flush() to dump everything
+ * on to the cache. The cache file is to be opened in HOME env variable
+ * inside a directory ".sdt".

I think we can share $HOME/.debug/ with buildid, which uses .debug/.build-id/*. So, perhaps, $HOME/.debug/.sdt-cache will be better filename (not dirname).

Thank you,

Quoted 87 lines

+ */

tools/perf/util/sdt.c new file +79 -1
+static int flush_hash_list_to_cache(struct hash_table *file_hash)
+{
+	FILE *cache;
+	int ret;
+	struct stat buf;
+	char sdt_cache_path[MAXPATHLEN], sdt_dir[MAXPATHLEN], *val;
+
+	val = getenv("HOME");
+	if (val) {
+		snprintf(sdt_dir, MAXPATHLEN-1, "%s/%s", val, SDT_CACHE_DIR);
+		snprintf(sdt_cache_path, MAXPATHLEN-1, "%s/%s",
+			 sdt_dir, SDT_FILE_CACHE);
+	} else {
+		pr_err("Error: Couldn't get the user's home directory\n");
+		ret = -1;
+		goto out_err;
+	}
+	ret = stat(sdt_dir, &buf);
+	if (ret) {
+		ret = mkdir(sdt_dir, buf.st_mode);
+		if (ret) {
+			pr_err("Error: Couldn't create %s\n", sdt_dir);
+			goto out_err;
+		}
+	}
+
+	cache = fopen(sdt_cache_path, "w");
+	if (!cache) {
+		pr_err("Error in creating %s file\n", sdt_cache_path);
+		ret = -errno;
+		goto out_err;
+	}
+
+	file_hash_list__flush(file_hash, cache);
+	fclose(cache);
+out_err:
+	return ret;
+}
+
+/**
+ * add_sdt_events: Add SDT events
+ * @arg: filename
+ *
+ * Initializes a hash table 'file_hash', calls add_to_hash_list() to add
+ * SDT events of @arg to the cache and then cleans them up.
+ * 'file_hash' is a hash table which maintains all the information
+ * related to the files with the SDT events in them. The file name serves
+ * as the key to this hash list. Each entry of the file_hash table is a
+ * list head which contains a chain of 'file_list' entries. Each 'file_list'
+ * entry contains the list of SDT events found in that file. This hash
+ * serves as a mapping from file name to the SDT events.
+ */
+int add_sdt_events(const char *arg)
+{
+	struct hash_table file_hash;
+	int ret, val;
+
+	/* Initialize the file hash_list */
+	ret = file_hash_list__init(&file_hash);
+	if (ret < 0) {
+		pr_err("Error: Couldn't initialize the SDT hash tables\n");
+		goto out;
+	}
+	/* Try to add the events to the file hash_list */
+	ret = add_to_hash_list(&file_hash, arg);
+	if (ret > 0) {
+		val = flush_hash_list_to_cache(&file_hash);
+		if (val < 0) {
+			ret = val;
+			goto out;
+		}
+		printf("%4d events added for %s\n", ret, arg);
+		ret = 0;
+	}
+
+out:
+	file_hash_list__cleanup(&file_hash);
+	return ret;
+}
 
--

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Research Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
#8 lore raw

PATCH v3 3/5 perf/sdt: Show SDT cache contents

In reply to Hemant Kumar

This patch adds support to dump the SDT cache onto sdtout. The cache data is read into a hash_list and then, it iterates through the hash_list to dump the data onto stdout.

# ./perf sdt-cache --dump

/usr/lib64/libc-2.16.so :
%libc:lll_futex_wake
%libc:longjmp_target
%libc:longjmp
%libc:lll_lock_wait_private
%libc:lll_futex_wake
%libc:longjmp_target
%libc:longjmp
%libc:setjmp

---
 tools/perf/Documentation/perf-sdt-cache.txt |    7 +++-
 tools/perf/builtin-sdt-cache.c              |   27 +++++++++++++++
 tools/perf/util/parse-events.h              |    1 +
 tools/perf/util/sdt.c                       |   48 +++++++++++++++++++++++++++
 4 files changed, 82 insertions(+), 1 deletion(-)
tools/perf/Documentation/perf-sdt-cache.txt +6 -1
index 08b9985..e57a867 100644
@@ -9,11 +9,13 @@ SYNOPSIS
 --------
 [verse]
 'perf sdt-cache --add <file_name>'
+'perf sdt-cache --dump'
 
 DESCRIPTION
 -----------
 This command manages the SDT events cache. It can add/remove SDT events
-associated with an ELF to the cache.
+associated with an ELF to the cache. It can also dump the SDT events' cache
+onto stdout.
 
 OPTIONS
 -------
@@ -21,6 +23,9 @@ OPTIONS
 --add::
         Add SDT events in the specified file to the cache. Takes file name
 	as an argument.
+-D::
+--dump::
+	Dump the contents of SDT events cache onto stdout.
 
 SEE ALSO
 --------
tools/perf/builtin-sdt-cache.c +27 -0
index 0e012f6..31041db 100644
@@ -16,6 +16,7 @@
 /* Session management structure */
 static struct {
 	bool add;
+	bool dump;
 	const char *target;
 } params;
 
@@ -28,6 +29,15 @@ static int opt_add_sdt_events(const struct option *opt __maybe_unused,
 	return 0;
 }
 
+static int opt_show_sdt_events(const struct option *opt __maybe_unused,
+			       const char *str, int unset __maybe_unused)
+{
+	if (str)
+		pr_err("Unknown option %s\n", str);
+	params.dump = true;
+	return 0;
+}
+
 int cmd_sdt_cache(int argc, const char **argv, const char *prefix __maybe_unused)
 {
 	int ret;
@@ -35,10 +45,14 @@ int cmd_sdt_cache(int argc, const char **argv, const char *prefix __maybe_unused
 		OPT_CALLBACK('a', "add", NULL, "filename",
 			     "add SDT events from a file.",
 			     opt_add_sdt_events),
+		OPT_CALLBACK_NOOPT('D', "dump", NULL, "show SDT events",
+				   "Read SDT events from cache and display.",
+				   opt_show_sdt_events),
 		OPT_END()
 	};
 	const char * const sdt_cache_usage[] = {
 		"perf sdt-cache --add filename",
+		"perf sd-cache --dump",
 		NULL
 	};
 
@@ -50,9 +64,22 @@ int cmd_sdt_cache(int argc, const char **argv, const char *prefix __maybe_unused
 
 	symbol__elf_init();
 	if (params.add) {
+		if (params.dump) {
+			pr_err("Error: Don't use --dump with --add\n");
+			usage_with_options(sdt_cache_usage, sdt_cache_options);
+		}
+
 		ret = add_sdt_events(params.target);
 		if (ret < 0)
 			pr_err("Cannot add SDT events to cache\n");
+	} else if (params.dump) {
+		if (argc == 0) {
+			ret = dump_sdt_events();
+			if (ret < 0)
+				pr_err("Cannot dump SDT event cache\n");
+		} else
+			usage_with_options(sdt_cache_usage, sdt_cache_options);
+
 	} else
 		usage_with_options(sdt_cache_usage, sdt_cache_options);
 	return 0;
tools/perf/util/parse-events.h +1 -0
index e6efe2c..f43e6aa 100644
@@ -110,5 +110,6 @@ extern int is_valid_tracepoint(const char *event_string);
 extern int valid_debugfs_mount(const char *debugfs);
 
 int add_sdt_events(const char *file);
+int dump_sdt_events(void);
 
 #endif /* __PERF_PARSE_EVENTS_H */
tools/perf/util/sdt.c +48 -0

51 lines not on this page · raw message

maintainer
#9 lore raw

Re: [PATCH v3 3/5] perf/sdt: Show SDT cache contents

Quoted 6 lines · Hemant Kumar

This patch adds support to dump the SDT cache onto sdtout. The cache data is read into a hash_list and then, it iterates through the hash_list to dump the data onto stdout.

# ./perf sdt-cache --dump
Quoted 9 lines

/usr/lib64/libc-2.16.so :
%libc:lll_futex_wake
%libc:longjmp_target
%libc:longjmp
%libc:lll_lock_wait_private
%libc:lll_futex_wake
%libc:longjmp_target
%libc:longjmp
%libc:setjmp

Hmm.. it seems the output is prefixed with '\t' in the code..

[SNIP]

tools/perf/builtin-sdt-cache.c int cmd_sdt_cache(int argc, const char **argv, const char *prefix __maybe_unused · line 55

Quoted 12 lines
tools/perf/builtin-sdt-cache.c +4 -0
@@ -35,10 +45,14 @@ int cmd_sdt_cache(int argc, const char **argv, const char *prefix __maybe_unused
 		OPT_CALLBACK('a', "add", NULL, "filename",
 			     "add SDT events from a file.",
 			     opt_add_sdt_events),
+		OPT_CALLBACK_NOOPT('D', "dump", NULL, "show SDT events",
+				   "Read SDT events from cache and display.",
+				   opt_show_sdt_events),
 		OPT_END()
 	};
 	const char * const sdt_cache_usage[] = {
 		"perf sdt-cache --add filename",
+		"perf sd-cache --dump",

s/sd-cache/sdt-cache/

Quoted 2 lines
(quoted diff) +0 -0
 		NULL
 	};

[SNIP]

Quoted 10 lines
tools/perf/util/sdt.c +10 -0
+static void file_hash_list__display(struct hash_table *file_hash)
+{
+	struct file_sdt_ent *file_pos;
+	struct list_head *sdt_head;
+	struct hlist_head *ent_head;
+	struct sdt_note *sdt_ptr;
+	int i;
+
+	/* Go through all entries */
+	for (i = 0; i < SDT_HASH_SIZE; i++) {

tools/perf/util/sdt.c out: · line 637

Quoted 10 lines
tools/perf/util/sdt.c +10 -0
+		/* Obtain the list head */
+		ent_head = &file_hash->ent[i];
+
+		/* ':' are used here as delimiters */
+		hlist_for_each_entry(file_pos, ent_head, file_list) {
+			fprintf(stdout, "%s:\n", file_pos->name);
+			sdt_head = &file_pos->sdt_list;
+			list_for_each_entry(sdt_ptr, sdt_head, note_list) {
+				fprintf(stdout, "\t%%%s:%s\n",
+					sdt_ptr->provider, sdt_ptr->name);

.. Here you printed it with \t.

Other than that, looks good to me.

Thanks,
Namhyung

Quoted 20 lines
tools/perf/util/sdt.c +20 -0
+			}
+			printf("\n");
+		}
+	}
+}
+
+/**
+ * dump_sdt_events: Dump the SDT events on stdout
+ */
+int dump_sdt_events(void)
+{
+	struct hash_table file_hash;
+	int ret;
+
+	ret = file_hash_list__init(&file_hash);
+	if (!ret)
+		file_hash_list__display(&file_hash);
+	file_hash_list__cleanup(&file_hash);
+	return ret;
+}
#10 lore raw

PATCH v3 4/5 perf/sdt: Delete SDT events from cache

In reply to Hemant Kumar

This patch adds the support to delete SDT events from the cache. To delete an event corresponding to a file, first the cache is read into the file_hash list. The key is calculated from the file name. And then, the file_list for that file_hash entry is traversed to find out the target file_list entry. Once, it is found, its contents are freed up.

# ./perf sdt-cache --del /usr/lib64/libc-2.16.so

8 events removed for /usr/lib64/libc-2.16.so

---
 tools/perf/Documentation/perf-sdt-cache.txt |    6 +++-
 tools/perf/builtin-sdt-cache.c              |   28 +++++++++++++++++++
 tools/perf/util/parse-events.h              |    1 +
 tools/perf/util/sdt.c                       |   39 +++++++++++++++++++++++++++
 4 files changed, 72 insertions(+), 2 deletions(-)
tools/perf/Documentation/perf-sdt-cache.txt +5 -1
index e57a867..523dcda 100644
@@ -8,7 +8,7 @@ perf-sdt-cache - Manage SDT events' cache.
 SYNOPSIS
 --------
 [verse]
-'perf sdt-cache --add <file_name>'
+'perf sdt-cache [--add | --del] <file_name>'
 'perf sdt-cache --dump'
 
 DESCRIPTION
@@ -23,6 +23,10 @@ OPTIONS
 --add::
         Add SDT events in the specified file to the cache. Takes file name
 	as an argument.
+-d::
+--del::
+        Remove SDT events in the specified file from the cache. Takes file as an
+	argument.
 -D::
 --dump::
 	Dump the contents of SDT events cache onto stdout.
tools/perf/builtin-sdt-cache.c +27 -1
index 31041db..b9ed5d4 100644
@@ -16,6 +16,7 @@
 /* Session management structure */
 static struct {
 	bool add;
+	bool del;
 	bool dump;
 	const char *target;
 } params;
@@ -29,6 +30,16 @@ static int opt_add_sdt_events(const struct option *opt __maybe_unused,
 	return 0;
 }
 
+static int opt_del_sdt_events(const struct option *opt __maybe_unused,
+			      const char *str, int unset __maybe_unused)
+{
+	params.del = true;
+	params.target = str;
+
+	return 0;
+}
+
+
 static int opt_show_sdt_events(const struct option *opt __maybe_unused,
 			       const char *str, int unset __maybe_unused)
 {
@@ -45,13 +56,16 @@ int cmd_sdt_cache(int argc, const char **argv, const char *prefix __maybe_unused
 		OPT_CALLBACK('a', "add", NULL, "filename",
 			     "add SDT events from a file.",
 			     opt_add_sdt_events),
+		OPT_CALLBACK('d', "del", NULL, "filename",
+			     "Remove SDT events corresponding to a file from the sdt cache.",
+			     opt_del_sdt_events),
 		OPT_CALLBACK_NOOPT('D', "dump", NULL, "show SDT events",
 				   "Read SDT events from cache and display.",
 				   opt_show_sdt_events),
 		OPT_END()
 	};
 	const char * const sdt_cache_usage[] = {
-		"perf sdt-cache --add filename",
+		"perf sdt-cache [--add | --del] filename",
 		"perf sd-cache --dump",
 		NULL
 	};
@@ -64,6 +78,10 @@ int cmd_sdt_cache(int argc, const char **argv, const char *prefix __maybe_unused
 
 	symbol__elf_init();
 	if (params.add) {
+		if (params.del) {
+			pr_err("Error: Don't use --del with --add\n");
+			usage_with_options(sdt_cache_usage, sdt_cache_options);
+		}
 		if (params.dump) {
 			pr_err("Error: Don't use --dump with --add\n");
 			usage_with_options(sdt_cache_usage, sdt_cache_options);
@@ -72,6 +90,14 @@ int cmd_sdt_cache(int argc, const char **argv, const char *prefix __maybe_unused
 		ret = add_sdt_events(params.target);
 		if (ret < 0)
 			pr_err("Cannot add SDT events to cache\n");
+	} else if (params.del) {
+		if (params.dump) {
+			pr_err("Error: Don't use --dump with --del\n");
+			usage_with_options(sdt_cache_usage, sdt_cache_options);
+		}
+		ret = remove_sdt_events(params.target);
+		if (ret < 0)
+			pr_err("Cannot remove SDT events from cache\n");
 	} else if (params.dump) {
 		if (argc == 0) {
 			ret = dump_sdt_events();
tools/perf/util/parse-events.h +1 -0
index f43e6aa..2297af7 100644
@@ -111,5 +111,6 @@ extern int valid_debugfs_mount(const char *debugfs);
 
 int add_sdt_events(const char *file);
 int dump_sdt_events(void);
+int remove_sdt_events(const char *str);
 
 #endif /* __PERF_PARSE_EVENTS_H */
tools/perf/util/sdt.c +39 -0
index 4e64f3e..880c089 100644
@@ -655,3 +655,42 @@ int dump_sdt_events(void)
 	file_hash_list__cleanup(&file_hash);
 	return ret;
 }
+
+/**
+ * remove_sdt_events: remove SDT events from cache
+ * @str: filename
+ *
+ * Removes the SDT events from the cache corresponding to the file name
+ * @str.
+ */
+int remove_sdt_events(const char *str)
+{
+	struct hash_table file_hash;
+	char *res_path;
+	int nr_del, ret = -ENOMEM;
+
+	res_path = realpath(str, NULL);
+	if (!res_path)
+		goto out_clean;
+
+	/* Initialize the hash_lists */
+	ret = file_hash_list__init(&file_hash);
+	if (ret < 0)
+		goto out;
+
+	/* Remove the file_list entry from file_hash and update the file_hash */
+	nr_del = file_hash_list__remove(&file_hash, res_path);
+	if (nr_del > 0) {
+		printf("%d event(s) removed for %s\n", nr_del, res_path);
+		flush_hash_list_to_cache(&file_hash);
+		ret = nr_del;
+		goto out;
+	} else if (!nr_del)
+		pr_err("Error: Events for %s not found\n", str);
+
+out:
+	free(res_path);
+out_clean:
+	file_hash_list__cleanup(&file_hash);
+	return ret;
+}
#11 lore raw

PATCH v3 5/5 perf/sdt: Add support to perf record to trace SDT events

In reply to Hemant Kumar

The SDT events are already stored in a cache file (/var/cache/perf/perf-sdt-file.cache).
Although the file_hash table helps in addition or deletion of SDT events from the cache, its not of much use when it comes to probing the actual SDT event, because the key to this hash list is a file name and not the SDT event name (which is given as an argument to perf record). So, we won't be able to hash into it.

To avoid this problem, we can create another hash list "event_hash" list which will be maintained along with the file_hash list.
Whenever a user invokes 'perf record -e %provider:event, perf should initialize the event_hash list and the file_hash list.
The key to event_hash list is calculated from the event name and its provider name.

            event_hash       sdt_note
            |---------|   ----------------
            |         |   |   file_ptr   |==> container file_sdt_ent
key = 129 =>| hlist ==|===|=> event_list=|==> to sdt notes hashed to
       	    |         |   |   name       |    same entry
            |---------|   |   provider   |
            |	      |   |   note_list==|==> to other notes in the
key = 130 =>| hlist   |   ---------------     same file
            |---------|

The entry at that key in event_hash contains a list of SDT notes hashed to the same entry. It compares the name and provider to see if that is the SDT note we are looking for. If yes, find out the file that contains this SDT note. There is a file_ptr pointer embedded in this note which points to the struct file_sdt_ent contained in the file_hash. From "file_sdt_ent" we will find out the file name. Convert this sdt note into a perf event and then write this into uprobe_events file to be able to record the event.
Then, corresponding entries are added to uprobe_events file for
the SDT events.
After recording is done, these events are silently deleted from uprobe_events file. The uprobe_events file is present in debugfs/tracing directory.

To support the addition and deletion of SDT events to/from uprobe_events file, a record_sdt struct is maintained which has the event data.

An example usage:

# ./perf record -e %user_app:fun_start -aR /home/user_app

In main
This is foo
This is fun
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.265 MB perf.data (~11581 samples) ]

# ./perf report --stdio
...
# To display the perf.data header info, please use --header/--header-only options.
#
# Samples: 1  of event 'user_app:fun_start'
# Event count (approx.): 1
#
# Overhead   Command  Shared Object   Symbol
# ........  ........  .............  .......
#
   100.00%  user_app  user_app       [.] fun

Multiple events can also be recorded simultaneously.

---
 tools/perf/builtin-record.c    |   22 +++++
 tools/perf/util/parse-events.c |    6 +
 tools/perf/util/parse-events.h |    3 +
 tools/perf/util/probe-event.c  |  121 +++++++++++++++++++++-----
 tools/perf/util/probe-event.h  |    7 ++
 tools/perf/util/probe-finder.c |    3 +
 tools/perf/util/sdt.c          |  186 ++++++++++++++++++++++++++++++++++++++--
 tools/perf/util/symbol.h       |    2 
 8 files changed, 319 insertions(+), 31 deletions(-)
tools/perf/builtin-record.c +22 -0
index 44c6f3d..b0f1cc8 100644
@@ -45,6 +45,26 @@ struct record {
 	long			samples;
 };
 
+/* Session specific to SDT tracing */
+struct record_sdt {
+	bool    sdt;    /* is this SDT event tracing? */
+	char    *str;   /* hold the event name */
+} rec_sdt;
+
+int trace_sdt_event(const char *str)
+{
+	int ret = 0;
+
+	rec_sdt.sdt = true;
+	rec_sdt.str = strdup(str);
+	if (!rec_sdt.str)
+		return -ENOMEM;
+	ret = event_hash_list__lookup(str);
+
+	return ret;
+}
+
+
 static int record__write(struct record *rec, void *bf, size_t size)
 {
 	if (perf_data_file__write(rec->session->file, bf, size) < 0) {
@@ -874,6 +894,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
 	}
 
 	err = __cmd_record(&record, argc, argv);
+	if (rec_sdt.sdt)
+		err = remove_perf_sdt_events(rec_sdt.str);
 out_symbol_exit:
 	perf_evlist__delete(rec->evlist);
 	symbol__exit();
tools/perf/util/parse-events.c +6 -0
index d76aa30..edc96d9 100644
@@ -900,6 +900,12 @@ static int parse_events__scanner(const char *str, void *data, int start_token)
 		return ret;
 
 	buffer = parse_events__scan_string(str, scanner);
+	/* '%' means it can be an SDT event */
+	if (*str == '%')
+		if (strchr(str, ':')) {
+			ret = trace_sdt_event(str);
+			str++;
+		}
 
 #ifdef PARSER_DEBUG
 	parse_events_debug = 1;
tools/perf/util/parse-events.h +3 -0
index 2297af7..52a163a 100644
@@ -112,5 +112,8 @@ extern int valid_debugfs_mount(const char *debugfs);
 int add_sdt_events(const char *file);
 int dump_sdt_events(void);
 int remove_sdt_events(const char *str);
+int event_hash_list__lookup(const char *str);
+int remove_perf_sdt_events(const char *str);
+int trace_sdt_event(const char *str);
 
 #endif /* __PERF_PARSE_EVENTS_H */
tools/perf/util/probe-event.c +98 -23

246 lines not on this page · raw message

tools/perf/util/probe-event.h +7 -0
index e01e994..d647042 100644
@@ -74,9 +74,15 @@ struct perf_probe_event {
 	struct perf_probe_point	point;	/* Probe point */
 	int			nargs;	/* Number of arguments */
 	bool			uprobes;
+	bool			sdt;	/* An SDT event? */
 	struct perf_probe_arg	*args;	/* Arguments */
 };
 
+struct perf_sdt_event {
+	struct sdt_note		*note;		/* SDT note info */
+	char			*file_name;	/* File name */
+};
+
 /* Line range */
 struct line_range {
 	char			*file;		/* File name */
@@ -135,6 +141,7 @@ extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
 			       struct strfilter *filter, bool externs);
 extern int show_available_funcs(const char *module, struct strfilter *filter,
 				bool user);
+extern int add_perf_sdt_event(struct perf_sdt_event *sev);
 
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX	1024
tools/perf/util/probe-finder.c +3 -0
index c7918f8..1dd89db8 100644
@@ -1197,6 +1197,9 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,
 	tf.tevs = *tevs;
 	tf.ntevs = 0;
 
+	/* Number of trace events for SDT event is 1 */
+	if (pev->sdt)
+		return 1;
 	ret = debuginfo__find_probes(dbg, &tf.pf);
 	if (ret < 0) {
 		zfree(tevs);
tools/perf/util/sdt.c +178 -8

250 lines not on this page · raw message

tools/perf/util/symbol.h +2 -0
index 23d3d41..c0dac7b 100644
@@ -323,6 +323,8 @@ struct sdt_note {
 		Elf32_Addr a32[3];
 	} addr;
 	struct list_head note_list;	/* SDT notes' list */
+	struct hlist_node event_list;	/* Link to event_hash_list entry */
+	struct file_sdt_ent *file_ptr;	/* ptr to the containing file_sdt_ent */
 };
 
 int get_sdt_note_list(struct list_head *head, const char *target);
#12 lore raw

Re: [PATCH v3 5/5] perf/sdt: Add support to perf record to trace SDT events

Hi Hemant,

Quoted 2 lines · Hemant Kumar

The SDT events are already stored in a cache file
(/var/cache/perf/perf-sdt-file.cache).

Please describe what this patch does at first.

Quoted 35 lines

Although the file_hash table helps in addition or deletion of SDT events from the cache, its not of much use when it comes to probing the actual SDT event, because the key to this hash list is a file name and not the SDT event name (which is given as an argument to perf record). So, we won't be able to hash into it.

To avoid this problem, we can create another hash list "event_hash" list which will be maintained along with the file_hash list.
Whenever a user invokes 'perf record -e %provider:event, perf should initialize the event_hash list and the file_hash list.
The key to event_hash list is calculated from the event name and its provider name.

            event_hash       sdt_note
            |---------|   ----------------
            |         |   |   file_ptr   |==> container file_sdt_ent
key = 129 =>| hlist ==|===|=> event_list=|==> to sdt notes hashed to
       	    |         |   |   name       |    same entry
            |---------|   |   provider   |
            |	      |   |   note_list==|==> to other notes in the
key = 130 =>| hlist   |   ---------------     same file
            |---------|

The entry at that key in event_hash contains a list of SDT notes hashed to the same entry. It compares the name and provider to see if that is the SDT note we are looking for. If yes, find out the file that contains this SDT note. There is a file_ptr pointer embedded in this note which points to the struct file_sdt_ent contained in the file_hash. From "file_sdt_ent" we will find out the file name. Convert this sdt note into a perf event and then write this into uprobe_events file to be able to record the event.
Then, corresponding entries are added to uprobe_events file for
the SDT events.
After recording is done, these events are silently deleted from uprobe_events file. The uprobe_events file is present in debugfs/tracing directory.

Quoted 2 lines

To support the addition and deletion of SDT events to/from uprobe_events file, a record_sdt struct is maintained which has the event data.

OK, I have some comments on this.

Quoted 3 lines

An example usage:

# ./perf record -e %user_app:fun_start -aR /home/user_app

At first, I'd like to add SDT support for adding probes too, like below;

./perf probe -a '%user_app:fun_start $vars'

So, maybe we don't need to remove the SDT-based events silently, nor hide it from users. I think you just need to add new sdt events and verify it if there is.

BTW, for silently adding event, I'll introduce --quite(-q) option for perf probe. So you'll just need to set silent flag with that.

Thank you,

Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Research Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
#13 lore raw

Re: [PATCH v3 5/5] perf/sdt: Add support to perf record to trace SDT events

Hi Masami,

Quoted 7 lines · Masami Hiramatsu

Hi Hemant,

Quoted 2 lines · Hemant Kumar

The SDT events are already stored in a cache file
(/var/cache/perf/perf-sdt-file.cache).

Please describe what this patch does at first.

Sure, will do that.

Quoted 37 lines
Quoted 35 lines

Although the file_hash table helps in addition or deletion of SDT events from the cache, its not of much use when it comes to probing the actual SDT event, because the key to this hash list is a file name and not the SDT event name (which is given as an argument to perf record). So, we won't be able to hash into it.

To avoid this problem, we can create another hash list "event_hash" list which will be maintained along with the file_hash list.
Whenever a user invokes 'perf record -e %provider:event, perf should initialize the event_hash list and the file_hash list.
The key to event_hash list is calculated from the event name and its provider name.

             event_hash       sdt_note
             |---------|   ----------------
             |         |   |   file_ptr   |==> container file_sdt_ent
key = 129 =>| hlist ==|===|=> event_list=|==> to sdt notes hashed to
        	    |         |   |   name       |    same entry
             |---------|   |   provider   |
             |	      |   |   note_list==|==> to other notes in the
key = 130 =>| hlist   |   ---------------     same file
             |---------|

The entry at that key in event_hash contains a list of SDT notes hashed to the same entry. It compares the name and provider to see if that is the SDT note we are looking for. If yes, find out the file that contains this SDT note. There is a file_ptr pointer embedded in this note which points to the struct file_sdt_ent contained in the file_hash. From "file_sdt_ent" we will find out the file name. Convert this sdt note into a perf event and then write this into uprobe_events file to be able to record the event.
Then, corresponding entries are added to uprobe_events file for
the SDT events.
After recording is done, these events are silently deleted from uprobe_events file. The uprobe_events file is present in debugfs/tracing directory.

Quoted 2 lines

To support the addition and deletion of SDT events to/from uprobe_events file, a record_sdt struct is maintained which has the event data.

Quoted 8 lines

OK, I have some comments on this.

Quoted 3 lines

An example usage:

# ./perf record -e %user_app:fun_start -aR /home/user_app

At first, I'd like to add SDT support for adding probes too, like below;

./perf probe -a '%user_app:fun_start $vars'

But I think, previously we discussed that we won't be having "perf probe" for SDT events. We list them and probe/trace them using "perf record" directly.

Quoted 11 lines

So, maybe we don't need to remove the SDT-based events silently, nor hide it from users. I think you just need to add new sdt events and verify it if there is.

BTW, for silently adding event, I'll introduce --quite(-q) option for perf probe. So you'll just need to set silent flag with that.

Thank you,

Signature · 2 lines
Thanks,
Hemant Kumar
#14 lore raw

Re: [PATCH v3 5/5] perf/sdt: Add support to perf record to trace SDT events

Quoted 14 lines · Hemant Kumar
Quoted 14 lines
Quoted 10 lines

From "file_sdt_ent" we will find out the file name.
Convert this sdt note into a perf event and then write this into uprobe_events file to be able to record the event.
Then, corresponding entries are added to uprobe_events file for the SDT events.
After recording is done, these events are silently deleted from uprobe_events file. The uprobe_events file is present in debugfs/tracing directory.

To support the addition and deletion of SDT events to/from uprobe_events file, a record_sdt struct is maintained which has the event data.

OK, I have some comments on this.

Quoted 2 lines

An example usage:

Quoted 8 lines
Quoted 4 lines
Quoted 1 line
# ./perf record -e %user_app:fun_start -aR /home/user_app

At first, I'd like to add SDT support for adding probes too, like below;

./perf probe -a '%user_app:fun_start $vars'

But I think, previously we discussed that we won't be having "perf probe" for SDT events.
We list them and probe/trace them using "perf record" directly.

Right, sorry for confusing you. I meant that I'd like to support SDT on both of perf-record and perf-probe :)
And even if we'll hide sdt related events via perf, users can access it via ftrace. So, I doubt that we can completely hide them, in that case, honesty is the best way;)

Thank you,

Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Research Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
#15 lore raw

Re: [PATCH v3 5/5] perf/sdt: Add support to perf record to trace SDT events

Quoted 16 lines · Masami Hiramatsu
Quoted 15 lines · Hemant Kumar
Quoted 15 lines
Quoted 10 lines

From "file_sdt_ent" we will find out the file name.
Convert this sdt note into a perf event and then write this into uprobe_events file to be able to record the event.
Then, corresponding entries are added to uprobe_events file for the SDT events.
After recording is done, these events are silently deleted from uprobe_events file. The uprobe_events file is present in debugfs/tracing directory.

To support the addition and deletion of SDT events to/from uprobe_events file, a record_sdt struct is maintained which has the event data.

OK, I have some comments on this.

Quoted 3 lines

An example usage:

# ./perf record -e %user_app:fun_start -aR /home/user_app
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

At first, I'd like to add SDT support for adding probes too, like below;

./perf probe -a '%user_app:fun_start $vars'

But I think, previously we discussed that we won't be having "perf probe" for SDT events.
We list them and probe/trace them using "perf record" directly.

Right, sorry for confusing you. I meant that I'd like to support SDT on both of perf-record and perf-probe :)

I plan to do this and add this subsequently but will it be okay if we go with the current implementation for the time being?

What do you think?

Quoted 6 lines

And even if we'll hide sdt related events via perf, users can access it via ftrace. So, I doubt that we can completely hide them, in that case, honesty is the best way;)

Thank you,

Signature · 2 lines
Thanks,
Hemant Kumar
#16 lore raw

Re: [PATCH v3 5/5] perf/sdt: Add support to perf record to trace SDT events

In reply to Masami Hiramatsu
Quoted 20 lines · Masami Hiramatsu
Quoted 19 lines · Hemant Kumar
Quoted 18 lines
Quoted 10 lines

From "file_sdt_ent" we will find out the file name.
Convert this sdt note into a perf event and then write this into uprobe_events file to be able to record the event.
Then, corresponding entries are added to uprobe_events file for the SDT events.
After recording is done, these events are silently deleted from uprobe_events file. The uprobe_events file is present in debugfs/tracing directory.

To support the addition and deletion of SDT events to/from uprobe_events file, a record_sdt struct is maintained which has the event data.

OK, I have some comments on this.

Quoted 3 lines

An example usage:

# ./perf record -e %user_app:fun_start -aR /home/user_app

At first, I'd like to add SDT support for adding probes too, like below;

./perf probe -a '%user_app:fun_start $vars'

Quoted 9 lines
Quoted 3 lines

But I think, previously we discussed that we won't be having "perf probe" for SDT events.
We list them and probe/trace them using "perf record" directly.

Right, sorry for confusing you. I meant that I'd like to support SDT on both of perf-record and perf-probe :)
And even if we'll hide sdt related events via perf, users can access it via ftrace. So, I doubt that we can completely hide them, in that case, honesty is the best way;)

I am somehow not able to figure out how perf probe comes into the current workflow.

I think the current design was
1. perf sdt-cache --add <file> (only once per file)
2. perf record -e <sdt-event>

So what is the additional thing that perf probe does or Is it going to replace any of the above steps?

--
Thanks and Regards
Srikar Dronamraju

#17 lore raw

Re: [PATCH v3 5/5] perf/sdt: Add support to perf record to trace SDT events

Quoted 35 lines · Srikar Dronamraju
Quoted 20 lines · Masami Hiramatsu
Quoted 19 lines · Hemant Kumar
Quoted 18 lines
Quoted 10 lines

From "file_sdt_ent" we will find out the file name.
Convert this sdt note into a perf event and then write this into uprobe_events file to be able to record the event.
Then, corresponding entries are added to uprobe_events file for the SDT events.
After recording is done, these events are silently deleted from uprobe_events file. The uprobe_events file is present in debugfs/tracing directory.

To support the addition and deletion of SDT events to/from uprobe_events file, a record_sdt struct is maintained which has the event data.

OK, I have some comments on this.

Quoted 3 lines

An example usage:

# ./perf record -e %user_app:fun_start -aR /home/user_app

At first, I'd like to add SDT support for adding probes too, like below;

./perf probe -a '%user_app:fun_start $vars'

Quoted 9 lines
Quoted 3 lines

But I think, previously we discussed that we won't be having "perf probe" for SDT events.
We list them and probe/trace them using "perf record" directly.

Right, sorry for confusing you. I meant that I'd like to support SDT on both of perf-record and perf-probe :)
And even if we'll hide sdt related events via perf, users can access it via ftrace. So, I doubt that we can completely hide them, in that case, honesty is the best way;)

I am somehow not able to figure out how perf probe comes into the current workflow.

Quoted 6 lines

I think the current design was
1. perf sdt-cache --add <file> (only once per file)
2. perf record -e <sdt-event>

So what is the additional thing that perf probe does or Is it going to replace any of the above steps?

3. perf probe -a <sdt-event>

And this will be done subsequently in this series (without user interface part). However, current implementation of 2. will do the following steps

s1. get sdt event data from sdt-cache
s2. set up sdt events with suppressing messages s3. do recording events
(s4. and hiding existing sdt events from perf-probe --list) s5. remove sdt events

So, what I proposed were ;
- to implement s2., we can introduce --quiet(-q) option and use it instead of ->sdt flag checking
- removing s4. and s5.
- and add verification of existing sdt events at s2. if needed.

This will simplify your patch and removing complex part of sdt-specific code. What would you think about this?

Thank you,

Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Research Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
maintainer
#18 lore raw

Re: [PATCH v3 5/5] perf/sdt: Add support to perf record to trace SDT events

Hi Masami,

Quoted 22 lines · Masami Hiramatsu
Quoted 9 lines · Srikar Dronamraju

I am somehow not able to figure out how perf probe comes into the current workflow.

I think the current design was
1. perf sdt-cache --add <file> (only once per file)
2. perf record -e <sdt-event>

So what is the additional thing that perf probe does or Is it going to replace any of the above steps?

3. perf probe -a <sdt-event>

And this will be done subsequently in this series (without user interface part). However, current implementation of 2. will do the following steps

s1. get sdt event data from sdt-cache
s2. set up sdt events with suppressing messages s3. do recording events
(s4. and hiding existing sdt events from perf-probe --list) s5. remove sdt events

Quoted 5 lines

So, what I proposed were ;
- to implement s2., we can introduce --quiet(-q) option and use it instead of ->sdt flag checking
- removing s4. and s5.
- and add verification of existing sdt events at s2. if needed.

I'm okay with removing the s4 but not sure about the s5. In that case, we might have many dynamic events in a system without noticing to users.

Thanks,
Namhyung

Quoted 5 lines

This will simplify your patch and removing complex part of sdt-specific code. What would you think about this?

Thank you,

#19 lore raw

Re: Re: [PATCH v3 5/5] perf/sdt: Add support to perf record to trace SDT events

Quoted 25 lines · Namhyung Kim

Hi Masami,

Quoted 22 lines · Masami Hiramatsu
Quoted 9 lines · Srikar Dronamraju

I am somehow not able to figure out how perf probe comes into the current workflow.

I think the current design was
1. perf sdt-cache --add <file> (only once per file)
2. perf record -e <sdt-event>

So what is the additional thing that perf probe does or Is it going to replace any of the above steps?

3. perf probe -a <sdt-event>

And this will be done subsequently in this series (without user interface part). However, current implementation of 2. will do the following steps

s1. get sdt event data from sdt-cache
s2. set up sdt events with suppressing messages s3. do recording events
(s4. and hiding existing sdt events from perf-probe --list) s5. remove sdt events

Quoted 8 lines
Quoted 5 lines

So, what I proposed were ;
- to implement s2., we can introduce --quiet(-q) option and use it instead of ->sdt flag checking
- removing s4. and s5.
- and add verification of existing sdt events at s2. if needed.

I'm okay with removing the s4 but not sure about the s5. In that case, we might have many dynamic events in a system without noticing to users.

Indeed, okey, so let's keep s5 then :)

Thank you,

Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Research Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]