diff options
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/util/symbol-elf.c | 252 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 22 |
2 files changed, 274 insertions, 0 deletions
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index b222552c7159..6f15b92cbf70 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -1789,6 +1789,258 @@ void kcore_extract__delete(struct kcore_extract *kce) | |||
1789 | unlink(kce->extract_filename); | 1789 | unlink(kce->extract_filename); |
1790 | } | 1790 | } |
1791 | 1791 | ||
1792 | /** | ||
1793 | * populate_sdt_note : Parse raw data and identify SDT note | ||
1794 | * @elf: elf of the opened file | ||
1795 | * @data: raw data of a section with description offset applied | ||
1796 | * @len: note description size | ||
1797 | * @type: type of the note | ||
1798 | * @sdt_notes: List to add the SDT note | ||
1799 | * | ||
1800 | * Responsible for parsing the @data in section .note.stapsdt in @elf and | ||
1801 | * if its an SDT note, it appends to @sdt_notes list. | ||
1802 | */ | ||
1803 | static int populate_sdt_note(Elf **elf, const char *data, size_t len, | ||
1804 | struct list_head *sdt_notes) | ||
1805 | { | ||
1806 | const char *provider, *name; | ||
1807 | struct sdt_note *tmp = NULL; | ||
1808 | GElf_Ehdr ehdr; | ||
1809 | GElf_Addr base_off = 0; | ||
1810 | GElf_Shdr shdr; | ||
1811 | int ret = -EINVAL; | ||
1812 | |||
1813 | union { | ||
1814 | Elf64_Addr a64[NR_ADDR]; | ||
1815 | Elf32_Addr a32[NR_ADDR]; | ||
1816 | } buf; | ||
1817 | |||
1818 | Elf_Data dst = { | ||
1819 | .d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT, | ||
1820 | .d_size = gelf_fsize((*elf), ELF_T_ADDR, NR_ADDR, EV_CURRENT), | ||
1821 | .d_off = 0, .d_align = 0 | ||
1822 | }; | ||
1823 | Elf_Data src = { | ||
1824 | .d_buf = (void *) data, .d_type = ELF_T_ADDR, | ||
1825 | .d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0, | ||
1826 | .d_align = 0 | ||
1827 | }; | ||
1828 | |||
1829 | tmp = (struct sdt_note *)calloc(1, sizeof(struct sdt_note)); | ||
1830 | if (!tmp) { | ||
1831 | ret = -ENOMEM; | ||
1832 | goto out_err; | ||
1833 | } | ||
1834 | |||
1835 | INIT_LIST_HEAD(&tmp->note_list); | ||
1836 | |||
1837 | if (len < dst.d_size + 3) | ||
1838 | goto out_free_note; | ||
1839 | |||
1840 | /* Translation from file representation to memory representation */ | ||
1841 | if (gelf_xlatetom(*elf, &dst, &src, | ||
1842 | elf_getident(*elf, NULL)[EI_DATA]) == NULL) { | ||
1843 | pr_err("gelf_xlatetom : %s\n", elf_errmsg(-1)); | ||
1844 | goto out_free_note; | ||
1845 | } | ||
1846 | |||
1847 | /* Populate the fields of sdt_note */ | ||
1848 | provider = data + dst.d_size; | ||
1849 | |||
1850 | name = (const char *)memchr(provider, '\0', data + len - provider); | ||
1851 | if (name++ == NULL) | ||
1852 | goto out_free_note; | ||
1853 | |||
1854 | tmp->provider = strdup(provider); | ||
1855 | if (!tmp->provider) { | ||
1856 | ret = -ENOMEM; | ||
1857 | goto out_free_note; | ||
1858 | } | ||
1859 | tmp->name = strdup(name); | ||
1860 | if (!tmp->name) { | ||
1861 | ret = -ENOMEM; | ||
1862 | goto out_free_prov; | ||
1863 | } | ||
1864 | |||
1865 | if (gelf_getclass(*elf) == ELFCLASS32) { | ||
1866 | memcpy(&tmp->addr, &buf, 3 * sizeof(Elf32_Addr)); | ||
1867 | tmp->bit32 = true; | ||
1868 | } else { | ||
1869 | memcpy(&tmp->addr, &buf, 3 * sizeof(Elf64_Addr)); | ||
1870 | tmp->bit32 = false; | ||
1871 | } | ||
1872 | |||
1873 | if (!gelf_getehdr(*elf, &ehdr)) { | ||
1874 | pr_debug("%s : cannot get elf header.\n", __func__); | ||
1875 | ret = -EBADF; | ||
1876 | goto out_free_name; | ||
1877 | } | ||
1878 | |||
1879 | /* Adjust the prelink effect : | ||
1880 | * Find out the .stapsdt.base section. | ||
1881 | * This scn will help us to handle prelinking (if present). | ||
1882 | * Compare the retrieved file offset of the base section with the | ||
1883 | * base address in the description of the SDT note. If its different, | ||
1884 | * then accordingly, adjust the note location. | ||
1885 | */ | ||
1886 | if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) { | ||
1887 | base_off = shdr.sh_offset; | ||
1888 | if (base_off) { | ||
1889 | if (tmp->bit32) | ||
1890 | tmp->addr.a32[0] = tmp->addr.a32[0] + base_off - | ||
1891 | tmp->addr.a32[1]; | ||
1892 | else | ||
1893 | tmp->addr.a64[0] = tmp->addr.a64[0] + base_off - | ||
1894 | tmp->addr.a64[1]; | ||
1895 | } | ||
1896 | } | ||
1897 | |||
1898 | list_add_tail(&tmp->note_list, sdt_notes); | ||
1899 | return 0; | ||
1900 | |||
1901 | out_free_name: | ||
1902 | free(tmp->name); | ||
1903 | out_free_prov: | ||
1904 | free(tmp->provider); | ||
1905 | out_free_note: | ||
1906 | free(tmp); | ||
1907 | out_err: | ||
1908 | return ret; | ||
1909 | } | ||
1910 | |||
1911 | /** | ||
1912 | * construct_sdt_notes_list : constructs a list of SDT notes | ||
1913 | * @elf : elf to look into | ||
1914 | * @sdt_notes : empty list_head | ||
1915 | * | ||
1916 | * Scans the sections in 'elf' for the section | ||
1917 | * .note.stapsdt. It, then calls populate_sdt_note to find | ||
1918 | * out the SDT events and populates the 'sdt_notes'. | ||
1919 | */ | ||
1920 | static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes) | ||
1921 | { | ||
1922 | GElf_Ehdr ehdr; | ||
1923 | Elf_Scn *scn = NULL; | ||
1924 | Elf_Data *data; | ||
1925 | GElf_Shdr shdr; | ||
1926 | size_t shstrndx, next; | ||
1927 | GElf_Nhdr nhdr; | ||
1928 | size_t name_off, desc_off, offset; | ||
1929 | int ret = 0; | ||
1930 | |||
1931 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
1932 | ret = -EBADF; | ||
1933 | goto out_ret; | ||
1934 | } | ||
1935 | if (elf_getshdrstrndx(elf, &shstrndx) != 0) { | ||
1936 | ret = -EBADF; | ||
1937 | goto out_ret; | ||
1938 | } | ||
1939 | |||
1940 | /* Look for the required section */ | ||
1941 | scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL); | ||
1942 | if (!scn) { | ||
1943 | ret = -ENOENT; | ||
1944 | goto out_ret; | ||
1945 | } | ||
1946 | |||
1947 | if ((shdr.sh_type != SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) { | ||
1948 | ret = -ENOENT; | ||
1949 | goto out_ret; | ||
1950 | } | ||
1951 | |||
1952 | data = elf_getdata(scn, NULL); | ||
1953 | |||
1954 | /* Get the SDT notes */ | ||
1955 | for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off, | ||
1956 | &desc_off)) > 0; offset = next) { | ||
1957 | if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) && | ||
1958 | !memcmp(data->d_buf + name_off, SDT_NOTE_NAME, | ||
1959 | sizeof(SDT_NOTE_NAME))) { | ||
1960 | /* Check the type of the note */ | ||
1961 | if (nhdr.n_type != SDT_NOTE_TYPE) | ||
1962 | goto out_ret; | ||
1963 | |||
1964 | ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off), | ||
1965 | nhdr.n_descsz, sdt_notes); | ||
1966 | if (ret < 0) | ||
1967 | goto out_ret; | ||
1968 | } | ||
1969 | } | ||
1970 | if (list_empty(sdt_notes)) | ||
1971 | ret = -ENOENT; | ||
1972 | |||
1973 | out_ret: | ||
1974 | return ret; | ||
1975 | } | ||
1976 | |||
1977 | /** | ||
1978 | * get_sdt_note_list : Wrapper to construct a list of sdt notes | ||
1979 | * @head : empty list_head | ||
1980 | * @target : file to find SDT notes from | ||
1981 | * | ||
1982 | * This opens the file, initializes | ||
1983 | * the ELF and then calls construct_sdt_notes_list. | ||
1984 | */ | ||
1985 | int get_sdt_note_list(struct list_head *head, const char *target) | ||
1986 | { | ||
1987 | Elf *elf; | ||
1988 | int fd, ret; | ||
1989 | |||
1990 | fd = open(target, O_RDONLY); | ||
1991 | if (fd < 0) | ||
1992 | return -EBADF; | ||
1993 | |||
1994 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1995 | if (!elf) { | ||
1996 | ret = -EBADF; | ||
1997 | goto out_close; | ||
1998 | } | ||
1999 | ret = construct_sdt_notes_list(elf, head); | ||
2000 | elf_end(elf); | ||
2001 | out_close: | ||
2002 | close(fd); | ||
2003 | return ret; | ||
2004 | } | ||
2005 | |||
2006 | /** | ||
2007 | * cleanup_sdt_note_list : free the sdt notes' list | ||
2008 | * @sdt_notes: sdt notes' list | ||
2009 | * | ||
2010 | * Free up the SDT notes in @sdt_notes. | ||
2011 | * Returns the number of SDT notes free'd. | ||
2012 | */ | ||
2013 | int cleanup_sdt_note_list(struct list_head *sdt_notes) | ||
2014 | { | ||
2015 | struct sdt_note *tmp, *pos; | ||
2016 | int nr_free = 0; | ||
2017 | |||
2018 | list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) { | ||
2019 | list_del(&pos->note_list); | ||
2020 | free(pos->name); | ||
2021 | free(pos->provider); | ||
2022 | free(pos); | ||
2023 | nr_free++; | ||
2024 | } | ||
2025 | return nr_free; | ||
2026 | } | ||
2027 | |||
2028 | /** | ||
2029 | * sdt_notes__get_count: Counts the number of sdt events | ||
2030 | * @start: list_head to sdt_notes list | ||
2031 | * | ||
2032 | * Returns the number of SDT notes in a list | ||
2033 | */ | ||
2034 | int sdt_notes__get_count(struct list_head *start) | ||
2035 | { | ||
2036 | struct sdt_note *sdt_ptr; | ||
2037 | int count = 0; | ||
2038 | |||
2039 | list_for_each_entry(sdt_ptr, start, note_list) | ||
2040 | count++; | ||
2041 | return count; | ||
2042 | } | ||
2043 | |||
1792 | void symbol__elf_init(void) | 2044 | void symbol__elf_init(void) |
1793 | { | 2045 | { |
1794 | elf_version(EV_CURRENT); | 2046 | elf_version(EV_CURRENT); |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index b10d558a8803..699f7cbcfe72 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -342,4 +342,26 @@ void arch__sym_update(struct symbol *s, GElf_Sym *sym); | |||
342 | 342 | ||
343 | int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb); | 343 | int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb); |
344 | 344 | ||
345 | /* structure containing an SDT note's info */ | ||
346 | struct sdt_note { | ||
347 | char *name; /* name of the note*/ | ||
348 | char *provider; /* provider name */ | ||
349 | bool bit32; /* whether the location is 32 bits? */ | ||
350 | union { /* location, base and semaphore addrs */ | ||
351 | Elf64_Addr a64[3]; | ||
352 | Elf32_Addr a32[3]; | ||
353 | } addr; | ||
354 | struct list_head note_list; /* SDT notes' list */ | ||
355 | }; | ||
356 | |||
357 | int get_sdt_note_list(struct list_head *head, const char *target); | ||
358 | int cleanup_sdt_note_list(struct list_head *sdt_notes); | ||
359 | int sdt_notes__get_count(struct list_head *start); | ||
360 | |||
361 | #define SDT_BASE_SCN ".stapsdt.base" | ||
362 | #define SDT_NOTE_SCN ".note.stapsdt" | ||
363 | #define SDT_NOTE_TYPE 3 | ||
364 | #define SDT_NOTE_NAME "stapsdt" | ||
365 | #define NR_ADDR 3 | ||
366 | |||
345 | #endif /* __PERF_SYMBOL */ | 367 | #endif /* __PERF_SYMBOL */ |