| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
|---|
| 2 | 2 | #include <asm/bug.h> |
|---|
| 3 | 3 | #include <linux/kernel.h> |
|---|
| 4 | +#include <linux/string.h> |
|---|
| 5 | +#include <linux/zalloc.h> |
|---|
| 4 | 6 | #include <sys/time.h> |
|---|
| 5 | 7 | #include <sys/resource.h> |
|---|
| 6 | 8 | #include <sys/types.h> |
|---|
| .. | .. |
|---|
| 8 | 10 | #include <unistd.h> |
|---|
| 9 | 11 | #include <errno.h> |
|---|
| 10 | 12 | #include <fcntl.h> |
|---|
| 13 | +#include <stdlib.h> |
|---|
| 14 | +#include <bpf/libbpf.h> |
|---|
| 15 | +#include "bpf-event.h" |
|---|
| 11 | 16 | #include "compress.h" |
|---|
| 17 | +#include "env.h" |
|---|
| 18 | +#include "namespaces.h" |
|---|
| 12 | 19 | #include "path.h" |
|---|
| 20 | +#include "map.h" |
|---|
| 13 | 21 | #include "symbol.h" |
|---|
| 14 | 22 | #include "srcline.h" |
|---|
| 15 | 23 | #include "dso.h" |
|---|
| 24 | +#include "dsos.h" |
|---|
| 16 | 25 | #include "machine.h" |
|---|
| 17 | 26 | #include "auxtrace.h" |
|---|
| 18 | | -#include "util.h" |
|---|
| 27 | +#include "util.h" /* O_CLOEXEC for older systems */ |
|---|
| 19 | 28 | #include "debug.h" |
|---|
| 20 | 29 | #include "string2.h" |
|---|
| 21 | 30 | #include "vdso.h" |
|---|
| .. | .. |
|---|
| 163 | 172 | break; |
|---|
| 164 | 173 | } |
|---|
| 165 | 174 | |
|---|
| 166 | | - build_id__sprintf(dso->build_id, |
|---|
| 167 | | - sizeof(dso->build_id), |
|---|
| 168 | | - build_id_hex); |
|---|
| 175 | + build_id__sprintf(&dso->bid, build_id_hex); |
|---|
| 169 | 176 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/"); |
|---|
| 170 | 177 | snprintf(filename + len, size - len, "%.2s/%s.debug", |
|---|
| 171 | 178 | build_id_hex, build_id_hex + 2); |
|---|
| .. | .. |
|---|
| 197 | 204 | case DSO_BINARY_TYPE__KALLSYMS: |
|---|
| 198 | 205 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: |
|---|
| 199 | 206 | case DSO_BINARY_TYPE__JAVA_JIT: |
|---|
| 207 | + case DSO_BINARY_TYPE__BPF_PROG_INFO: |
|---|
| 208 | + case DSO_BINARY_TYPE__BPF_IMAGE: |
|---|
| 209 | + case DSO_BINARY_TYPE__OOL: |
|---|
| 200 | 210 | case DSO_BINARY_TYPE__NOT_FOUND: |
|---|
| 201 | 211 | ret = -1; |
|---|
| 202 | 212 | break; |
|---|
| .. | .. |
|---|
| 404 | 414 | return -ENOMEM; |
|---|
| 405 | 415 | } |
|---|
| 406 | 416 | |
|---|
| 407 | | - strxfrchar(m->name, '-', '_'); |
|---|
| 417 | + strreplace(m->name, '-', '_'); |
|---|
| 408 | 418 | } |
|---|
| 409 | 419 | |
|---|
| 410 | 420 | return 0; |
|---|
| .. | .. |
|---|
| 442 | 452 | |
|---|
| 443 | 453 | static void dso__list_del(struct dso *dso) |
|---|
| 444 | 454 | { |
|---|
| 445 | | - list_del(&dso->data.open_entry); |
|---|
| 455 | + list_del_init(&dso->data.open_entry); |
|---|
| 446 | 456 | WARN_ONCE(dso__data_open_cnt <= 0, |
|---|
| 447 | 457 | "DSO data fd counter out of bounds."); |
|---|
| 448 | 458 | dso__data_open_cnt--; |
|---|
| .. | .. |
|---|
| 718 | 728 | return false; |
|---|
| 719 | 729 | } |
|---|
| 720 | 730 | |
|---|
| 731 | +static ssize_t bpf_read(struct dso *dso, u64 offset, char *data) |
|---|
| 732 | +{ |
|---|
| 733 | + struct bpf_prog_info_node *node; |
|---|
| 734 | + ssize_t size = DSO__DATA_CACHE_SIZE; |
|---|
| 735 | + u64 len; |
|---|
| 736 | + u8 *buf; |
|---|
| 737 | + |
|---|
| 738 | + node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, dso->bpf_prog.id); |
|---|
| 739 | + if (!node || !node->info_linear) { |
|---|
| 740 | + dso->data.status = DSO_DATA_STATUS_ERROR; |
|---|
| 741 | + return -1; |
|---|
| 742 | + } |
|---|
| 743 | + |
|---|
| 744 | + len = node->info_linear->info.jited_prog_len; |
|---|
| 745 | + buf = (u8 *)(uintptr_t)node->info_linear->info.jited_prog_insns; |
|---|
| 746 | + |
|---|
| 747 | + if (offset >= len) |
|---|
| 748 | + return -1; |
|---|
| 749 | + |
|---|
| 750 | + size = (ssize_t)min(len - offset, (u64)size); |
|---|
| 751 | + memcpy(data, buf + offset, size); |
|---|
| 752 | + return size; |
|---|
| 753 | +} |
|---|
| 754 | + |
|---|
| 755 | +static int bpf_size(struct dso *dso) |
|---|
| 756 | +{ |
|---|
| 757 | + struct bpf_prog_info_node *node; |
|---|
| 758 | + |
|---|
| 759 | + node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, dso->bpf_prog.id); |
|---|
| 760 | + if (!node || !node->info_linear) { |
|---|
| 761 | + dso->data.status = DSO_DATA_STATUS_ERROR; |
|---|
| 762 | + return -1; |
|---|
| 763 | + } |
|---|
| 764 | + |
|---|
| 765 | + dso->data.file_size = node->info_linear->info.jited_prog_len; |
|---|
| 766 | + return 0; |
|---|
| 767 | +} |
|---|
| 768 | + |
|---|
| 721 | 769 | static void |
|---|
| 722 | 770 | dso_cache__free(struct dso *dso) |
|---|
| 723 | 771 | { |
|---|
| .. | .. |
|---|
| 736 | 784 | pthread_mutex_unlock(&dso->lock); |
|---|
| 737 | 785 | } |
|---|
| 738 | 786 | |
|---|
| 739 | | -static struct dso_cache *dso_cache__find(struct dso *dso, u64 offset) |
|---|
| 787 | +static struct dso_cache *__dso_cache__find(struct dso *dso, u64 offset) |
|---|
| 740 | 788 | { |
|---|
| 741 | 789 | const struct rb_root *root = &dso->data.cache; |
|---|
| 742 | 790 | struct rb_node * const *p = &root->rb_node; |
|---|
| .. | .. |
|---|
| 795 | 843 | return cache; |
|---|
| 796 | 844 | } |
|---|
| 797 | 845 | |
|---|
| 798 | | -static ssize_t |
|---|
| 799 | | -dso_cache__memcpy(struct dso_cache *cache, u64 offset, |
|---|
| 800 | | - u8 *data, u64 size) |
|---|
| 846 | +static ssize_t dso_cache__memcpy(struct dso_cache *cache, u64 offset, u8 *data, |
|---|
| 847 | + u64 size, bool out) |
|---|
| 801 | 848 | { |
|---|
| 802 | 849 | u64 cache_offset = offset - cache->offset; |
|---|
| 803 | 850 | u64 cache_size = min(cache->size - cache_offset, size); |
|---|
| 804 | 851 | |
|---|
| 805 | | - memcpy(data, cache->data + cache_offset, cache_size); |
|---|
| 852 | + if (out) |
|---|
| 853 | + memcpy(data, cache->data + cache_offset, cache_size); |
|---|
| 854 | + else |
|---|
| 855 | + memcpy(cache->data + cache_offset, data, cache_size); |
|---|
| 806 | 856 | return cache_size; |
|---|
| 807 | 857 | } |
|---|
| 808 | 858 | |
|---|
| 809 | | -static ssize_t |
|---|
| 810 | | -dso_cache__read(struct dso *dso, struct machine *machine, |
|---|
| 811 | | - u64 offset, u8 *data, ssize_t size) |
|---|
| 859 | +static ssize_t file_read(struct dso *dso, struct machine *machine, |
|---|
| 860 | + u64 offset, char *data) |
|---|
| 812 | 861 | { |
|---|
| 813 | | - struct dso_cache *cache; |
|---|
| 814 | | - struct dso_cache *old; |
|---|
| 815 | 862 | ssize_t ret; |
|---|
| 816 | 863 | |
|---|
| 817 | | - do { |
|---|
| 818 | | - u64 cache_offset; |
|---|
| 864 | + pthread_mutex_lock(&dso__data_open_lock); |
|---|
| 819 | 865 | |
|---|
| 820 | | - cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); |
|---|
| 821 | | - if (!cache) |
|---|
| 822 | | - return -ENOMEM; |
|---|
| 866 | + /* |
|---|
| 867 | + * dso->data.fd might be closed if other thread opened another |
|---|
| 868 | + * file (dso) due to open file limit (RLIMIT_NOFILE). |
|---|
| 869 | + */ |
|---|
| 870 | + try_to_open_dso(dso, machine); |
|---|
| 823 | 871 | |
|---|
| 824 | | - pthread_mutex_lock(&dso__data_open_lock); |
|---|
| 825 | | - |
|---|
| 826 | | - /* |
|---|
| 827 | | - * dso->data.fd might be closed if other thread opened another |
|---|
| 828 | | - * file (dso) due to open file limit (RLIMIT_NOFILE). |
|---|
| 829 | | - */ |
|---|
| 830 | | - try_to_open_dso(dso, machine); |
|---|
| 831 | | - |
|---|
| 832 | | - if (dso->data.fd < 0) { |
|---|
| 833 | | - ret = -errno; |
|---|
| 834 | | - dso->data.status = DSO_DATA_STATUS_ERROR; |
|---|
| 835 | | - break; |
|---|
| 836 | | - } |
|---|
| 837 | | - |
|---|
| 838 | | - cache_offset = offset & DSO__DATA_CACHE_MASK; |
|---|
| 839 | | - |
|---|
| 840 | | - ret = pread(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE, cache_offset); |
|---|
| 841 | | - if (ret <= 0) |
|---|
| 842 | | - break; |
|---|
| 843 | | - |
|---|
| 844 | | - cache->offset = cache_offset; |
|---|
| 845 | | - cache->size = ret; |
|---|
| 846 | | - } while (0); |
|---|
| 847 | | - |
|---|
| 848 | | - pthread_mutex_unlock(&dso__data_open_lock); |
|---|
| 849 | | - |
|---|
| 850 | | - if (ret > 0) { |
|---|
| 851 | | - old = dso_cache__insert(dso, cache); |
|---|
| 852 | | - if (old) { |
|---|
| 853 | | - /* we lose the race */ |
|---|
| 854 | | - free(cache); |
|---|
| 855 | | - cache = old; |
|---|
| 856 | | - } |
|---|
| 857 | | - |
|---|
| 858 | | - ret = dso_cache__memcpy(cache, offset, data, size); |
|---|
| 872 | + if (dso->data.fd < 0) { |
|---|
| 873 | + dso->data.status = DSO_DATA_STATUS_ERROR; |
|---|
| 874 | + ret = -errno; |
|---|
| 875 | + goto out; |
|---|
| 859 | 876 | } |
|---|
| 860 | 877 | |
|---|
| 861 | | - if (ret <= 0) |
|---|
| 862 | | - free(cache); |
|---|
| 863 | | - |
|---|
| 878 | + ret = pread(dso->data.fd, data, DSO__DATA_CACHE_SIZE, offset); |
|---|
| 879 | +out: |
|---|
| 880 | + pthread_mutex_unlock(&dso__data_open_lock); |
|---|
| 864 | 881 | return ret; |
|---|
| 865 | 882 | } |
|---|
| 866 | 883 | |
|---|
| 867 | | -static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, |
|---|
| 868 | | - u64 offset, u8 *data, ssize_t size) |
|---|
| 884 | +static struct dso_cache *dso_cache__populate(struct dso *dso, |
|---|
| 885 | + struct machine *machine, |
|---|
| 886 | + u64 offset, ssize_t *ret) |
|---|
| 887 | +{ |
|---|
| 888 | + u64 cache_offset = offset & DSO__DATA_CACHE_MASK; |
|---|
| 889 | + struct dso_cache *cache; |
|---|
| 890 | + struct dso_cache *old; |
|---|
| 891 | + |
|---|
| 892 | + cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); |
|---|
| 893 | + if (!cache) { |
|---|
| 894 | + *ret = -ENOMEM; |
|---|
| 895 | + return NULL; |
|---|
| 896 | + } |
|---|
| 897 | + |
|---|
| 898 | + if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) |
|---|
| 899 | + *ret = bpf_read(dso, cache_offset, cache->data); |
|---|
| 900 | + else if (dso->binary_type == DSO_BINARY_TYPE__OOL) |
|---|
| 901 | + *ret = DSO__DATA_CACHE_SIZE; |
|---|
| 902 | + else |
|---|
| 903 | + *ret = file_read(dso, machine, cache_offset, cache->data); |
|---|
| 904 | + |
|---|
| 905 | + if (*ret <= 0) { |
|---|
| 906 | + free(cache); |
|---|
| 907 | + return NULL; |
|---|
| 908 | + } |
|---|
| 909 | + |
|---|
| 910 | + cache->offset = cache_offset; |
|---|
| 911 | + cache->size = *ret; |
|---|
| 912 | + |
|---|
| 913 | + old = dso_cache__insert(dso, cache); |
|---|
| 914 | + if (old) { |
|---|
| 915 | + /* we lose the race */ |
|---|
| 916 | + free(cache); |
|---|
| 917 | + cache = old; |
|---|
| 918 | + } |
|---|
| 919 | + |
|---|
| 920 | + return cache; |
|---|
| 921 | +} |
|---|
| 922 | + |
|---|
| 923 | +static struct dso_cache *dso_cache__find(struct dso *dso, |
|---|
| 924 | + struct machine *machine, |
|---|
| 925 | + u64 offset, |
|---|
| 926 | + ssize_t *ret) |
|---|
| 927 | +{ |
|---|
| 928 | + struct dso_cache *cache = __dso_cache__find(dso, offset); |
|---|
| 929 | + |
|---|
| 930 | + return cache ? cache : dso_cache__populate(dso, machine, offset, ret); |
|---|
| 931 | +} |
|---|
| 932 | + |
|---|
| 933 | +static ssize_t dso_cache_io(struct dso *dso, struct machine *machine, |
|---|
| 934 | + u64 offset, u8 *data, ssize_t size, bool out) |
|---|
| 869 | 935 | { |
|---|
| 870 | 936 | struct dso_cache *cache; |
|---|
| 937 | + ssize_t ret = 0; |
|---|
| 871 | 938 | |
|---|
| 872 | | - cache = dso_cache__find(dso, offset); |
|---|
| 873 | | - if (cache) |
|---|
| 874 | | - return dso_cache__memcpy(cache, offset, data, size); |
|---|
| 875 | | - else |
|---|
| 876 | | - return dso_cache__read(dso, machine, offset, data, size); |
|---|
| 939 | + cache = dso_cache__find(dso, machine, offset, &ret); |
|---|
| 940 | + if (!cache) |
|---|
| 941 | + return ret; |
|---|
| 942 | + |
|---|
| 943 | + return dso_cache__memcpy(cache, offset, data, size, out); |
|---|
| 877 | 944 | } |
|---|
| 878 | 945 | |
|---|
| 879 | 946 | /* |
|---|
| 880 | 947 | * Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks |
|---|
| 881 | 948 | * in the rb_tree. Any read to already cached data is served |
|---|
| 882 | | - * by cached data. |
|---|
| 949 | + * by cached data. Writes update the cache only, not the backing file. |
|---|
| 883 | 950 | */ |
|---|
| 884 | | -static ssize_t cached_read(struct dso *dso, struct machine *machine, |
|---|
| 885 | | - u64 offset, u8 *data, ssize_t size) |
|---|
| 951 | +static ssize_t cached_io(struct dso *dso, struct machine *machine, |
|---|
| 952 | + u64 offset, u8 *data, ssize_t size, bool out) |
|---|
| 886 | 953 | { |
|---|
| 887 | 954 | ssize_t r = 0; |
|---|
| 888 | 955 | u8 *p = data; |
|---|
| .. | .. |
|---|
| 890 | 957 | do { |
|---|
| 891 | 958 | ssize_t ret; |
|---|
| 892 | 959 | |
|---|
| 893 | | - ret = dso_cache_read(dso, machine, offset, p, size); |
|---|
| 960 | + ret = dso_cache_io(dso, machine, offset, p, size, out); |
|---|
| 894 | 961 | if (ret < 0) |
|---|
| 895 | 962 | return ret; |
|---|
| 896 | 963 | |
|---|
| .. | .. |
|---|
| 910 | 977 | return r; |
|---|
| 911 | 978 | } |
|---|
| 912 | 979 | |
|---|
| 913 | | -static int data_file_size(struct dso *dso, struct machine *machine) |
|---|
| 980 | +static int file_size(struct dso *dso, struct machine *machine) |
|---|
| 914 | 981 | { |
|---|
| 915 | 982 | int ret = 0; |
|---|
| 916 | 983 | struct stat st; |
|---|
| 917 | 984 | char sbuf[STRERR_BUFSIZE]; |
|---|
| 918 | | - |
|---|
| 919 | | - if (dso->data.file_size) |
|---|
| 920 | | - return 0; |
|---|
| 921 | | - |
|---|
| 922 | | - if (dso->data.status == DSO_DATA_STATUS_ERROR) |
|---|
| 923 | | - return -1; |
|---|
| 924 | 985 | |
|---|
| 925 | 986 | pthread_mutex_lock(&dso__data_open_lock); |
|---|
| 926 | 987 | |
|---|
| .. | .. |
|---|
| 950 | 1011 | return ret; |
|---|
| 951 | 1012 | } |
|---|
| 952 | 1013 | |
|---|
| 1014 | +int dso__data_file_size(struct dso *dso, struct machine *machine) |
|---|
| 1015 | +{ |
|---|
| 1016 | + if (dso->data.file_size) |
|---|
| 1017 | + return 0; |
|---|
| 1018 | + |
|---|
| 1019 | + if (dso->data.status == DSO_DATA_STATUS_ERROR) |
|---|
| 1020 | + return -1; |
|---|
| 1021 | + |
|---|
| 1022 | + if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) |
|---|
| 1023 | + return bpf_size(dso); |
|---|
| 1024 | + |
|---|
| 1025 | + return file_size(dso, machine); |
|---|
| 1026 | +} |
|---|
| 1027 | + |
|---|
| 953 | 1028 | /** |
|---|
| 954 | 1029 | * dso__data_size - Return dso data size |
|---|
| 955 | 1030 | * @dso: dso object |
|---|
| .. | .. |
|---|
| 959 | 1034 | */ |
|---|
| 960 | 1035 | off_t dso__data_size(struct dso *dso, struct machine *machine) |
|---|
| 961 | 1036 | { |
|---|
| 962 | | - if (data_file_size(dso, machine)) |
|---|
| 1037 | + if (dso__data_file_size(dso, machine)) |
|---|
| 963 | 1038 | return -1; |
|---|
| 964 | 1039 | |
|---|
| 965 | 1040 | /* For now just estimate dso data size is close to file size */ |
|---|
| 966 | 1041 | return dso->data.file_size; |
|---|
| 967 | 1042 | } |
|---|
| 968 | 1043 | |
|---|
| 969 | | -static ssize_t data_read_offset(struct dso *dso, struct machine *machine, |
|---|
| 970 | | - u64 offset, u8 *data, ssize_t size) |
|---|
| 1044 | +static ssize_t data_read_write_offset(struct dso *dso, struct machine *machine, |
|---|
| 1045 | + u64 offset, u8 *data, ssize_t size, |
|---|
| 1046 | + bool out) |
|---|
| 971 | 1047 | { |
|---|
| 972 | | - if (data_file_size(dso, machine)) |
|---|
| 1048 | + if (dso__data_file_size(dso, machine)) |
|---|
| 973 | 1049 | return -1; |
|---|
| 974 | 1050 | |
|---|
| 975 | 1051 | /* Check the offset sanity. */ |
|---|
| .. | .. |
|---|
| 979 | 1055 | if (offset + size < offset) |
|---|
| 980 | 1056 | return -1; |
|---|
| 981 | 1057 | |
|---|
| 982 | | - return cached_read(dso, machine, offset, data, size); |
|---|
| 1058 | + return cached_io(dso, machine, offset, data, size, out); |
|---|
| 983 | 1059 | } |
|---|
| 984 | 1060 | |
|---|
| 985 | 1061 | /** |
|---|
| .. | .. |
|---|
| 999 | 1075 | if (dso->data.status == DSO_DATA_STATUS_ERROR) |
|---|
| 1000 | 1076 | return -1; |
|---|
| 1001 | 1077 | |
|---|
| 1002 | | - return data_read_offset(dso, machine, offset, data, size); |
|---|
| 1078 | + return data_read_write_offset(dso, machine, offset, data, size, true); |
|---|
| 1003 | 1079 | } |
|---|
| 1004 | 1080 | |
|---|
| 1005 | 1081 | /** |
|---|
| .. | .. |
|---|
| 1018 | 1094 | { |
|---|
| 1019 | 1095 | u64 offset = map->map_ip(map, addr); |
|---|
| 1020 | 1096 | return dso__data_read_offset(dso, machine, offset, data, size); |
|---|
| 1097 | +} |
|---|
| 1098 | + |
|---|
| 1099 | +/** |
|---|
| 1100 | + * dso__data_write_cache_offs - Write data to dso data cache at file offset |
|---|
| 1101 | + * @dso: dso object |
|---|
| 1102 | + * @machine: machine object |
|---|
| 1103 | + * @offset: file offset |
|---|
| 1104 | + * @data: buffer to write |
|---|
| 1105 | + * @size: size of the @data buffer |
|---|
| 1106 | + * |
|---|
| 1107 | + * Write into the dso file data cache, but do not change the file itself. |
|---|
| 1108 | + */ |
|---|
| 1109 | +ssize_t dso__data_write_cache_offs(struct dso *dso, struct machine *machine, |
|---|
| 1110 | + u64 offset, const u8 *data_in, ssize_t size) |
|---|
| 1111 | +{ |
|---|
| 1112 | + u8 *data = (u8 *)data_in; /* cast away const to use same fns for r/w */ |
|---|
| 1113 | + |
|---|
| 1114 | + if (dso->data.status == DSO_DATA_STATUS_ERROR) |
|---|
| 1115 | + return -1; |
|---|
| 1116 | + |
|---|
| 1117 | + return data_read_write_offset(dso, machine, offset, data, size, false); |
|---|
| 1118 | +} |
|---|
| 1119 | + |
|---|
| 1120 | +/** |
|---|
| 1121 | + * dso__data_write_cache_addr - Write data to dso data cache at dso address |
|---|
| 1122 | + * @dso: dso object |
|---|
| 1123 | + * @machine: machine object |
|---|
| 1124 | + * @add: virtual memory address |
|---|
| 1125 | + * @data: buffer to write |
|---|
| 1126 | + * @size: size of the @data buffer |
|---|
| 1127 | + * |
|---|
| 1128 | + * External interface to write into the dso file data cache, but do not change |
|---|
| 1129 | + * the file itself. |
|---|
| 1130 | + */ |
|---|
| 1131 | +ssize_t dso__data_write_cache_addr(struct dso *dso, struct map *map, |
|---|
| 1132 | + struct machine *machine, u64 addr, |
|---|
| 1133 | + const u8 *data, ssize_t size) |
|---|
| 1134 | +{ |
|---|
| 1135 | + u64 offset = map->map_ip(map, addr); |
|---|
| 1136 | + return dso__data_write_cache_offs(dso, machine, offset, data, size); |
|---|
| 1021 | 1137 | } |
|---|
| 1022 | 1138 | |
|---|
| 1023 | 1139 | struct map *dso__new_map(const char *name) |
|---|
| .. | .. |
|---|
| 1053 | 1169 | return dso; |
|---|
| 1054 | 1170 | } |
|---|
| 1055 | 1171 | |
|---|
| 1056 | | -/* |
|---|
| 1057 | | - * Find a matching entry and/or link current entry to RB tree. |
|---|
| 1058 | | - * Either one of the dso or name parameter must be non-NULL or the |
|---|
| 1059 | | - * function will not work. |
|---|
| 1060 | | - */ |
|---|
| 1061 | | -static struct dso *__dso__findlink_by_longname(struct rb_root *root, |
|---|
| 1062 | | - struct dso *dso, const char *name) |
|---|
| 1063 | | -{ |
|---|
| 1064 | | - struct rb_node **p = &root->rb_node; |
|---|
| 1065 | | - struct rb_node *parent = NULL; |
|---|
| 1066 | | - |
|---|
| 1067 | | - if (!name) |
|---|
| 1068 | | - name = dso->long_name; |
|---|
| 1069 | | - /* |
|---|
| 1070 | | - * Find node with the matching name |
|---|
| 1071 | | - */ |
|---|
| 1072 | | - while (*p) { |
|---|
| 1073 | | - struct dso *this = rb_entry(*p, struct dso, rb_node); |
|---|
| 1074 | | - int rc = strcmp(name, this->long_name); |
|---|
| 1075 | | - |
|---|
| 1076 | | - parent = *p; |
|---|
| 1077 | | - if (rc == 0) { |
|---|
| 1078 | | - /* |
|---|
| 1079 | | - * In case the new DSO is a duplicate of an existing |
|---|
| 1080 | | - * one, print a one-time warning & put the new entry |
|---|
| 1081 | | - * at the end of the list of duplicates. |
|---|
| 1082 | | - */ |
|---|
| 1083 | | - if (!dso || (dso == this)) |
|---|
| 1084 | | - return this; /* Find matching dso */ |
|---|
| 1085 | | - /* |
|---|
| 1086 | | - * The core kernel DSOs may have duplicated long name. |
|---|
| 1087 | | - * In this case, the short name should be different. |
|---|
| 1088 | | - * Comparing the short names to differentiate the DSOs. |
|---|
| 1089 | | - */ |
|---|
| 1090 | | - rc = strcmp(dso->short_name, this->short_name); |
|---|
| 1091 | | - if (rc == 0) { |
|---|
| 1092 | | - pr_err("Duplicated dso name: %s\n", name); |
|---|
| 1093 | | - return NULL; |
|---|
| 1094 | | - } |
|---|
| 1095 | | - } |
|---|
| 1096 | | - if (rc < 0) |
|---|
| 1097 | | - p = &parent->rb_left; |
|---|
| 1098 | | - else |
|---|
| 1099 | | - p = &parent->rb_right; |
|---|
| 1100 | | - } |
|---|
| 1101 | | - if (dso) { |
|---|
| 1102 | | - /* Add new node and rebalance tree */ |
|---|
| 1103 | | - rb_link_node(&dso->rb_node, parent, p); |
|---|
| 1104 | | - rb_insert_color(&dso->rb_node, root); |
|---|
| 1105 | | - dso->root = root; |
|---|
| 1106 | | - } |
|---|
| 1107 | | - return NULL; |
|---|
| 1108 | | -} |
|---|
| 1109 | | - |
|---|
| 1110 | | -static inline struct dso *__dso__find_by_longname(struct rb_root *root, |
|---|
| 1111 | | - const char *name) |
|---|
| 1112 | | -{ |
|---|
| 1113 | | - return __dso__findlink_by_longname(root, NULL, name); |
|---|
| 1114 | | -} |
|---|
| 1115 | | - |
|---|
| 1116 | | -void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) |
|---|
| 1172 | +static void dso__set_long_name_id(struct dso *dso, const char *name, struct dso_id *id, bool name_allocated) |
|---|
| 1117 | 1173 | { |
|---|
| 1118 | 1174 | struct rb_root *root = dso->root; |
|---|
| 1119 | 1175 | |
|---|
| .. | .. |
|---|
| 1126 | 1182 | if (root) { |
|---|
| 1127 | 1183 | rb_erase(&dso->rb_node, root); |
|---|
| 1128 | 1184 | /* |
|---|
| 1129 | | - * __dso__findlink_by_longname() isn't guaranteed to add it |
|---|
| 1130 | | - * back, so a clean removal is required here. |
|---|
| 1185 | + * __dsos__findnew_link_by_longname_id() isn't guaranteed to |
|---|
| 1186 | + * add it back, so a clean removal is required here. |
|---|
| 1131 | 1187 | */ |
|---|
| 1132 | 1188 | RB_CLEAR_NODE(&dso->rb_node); |
|---|
| 1133 | 1189 | dso->root = NULL; |
|---|
| .. | .. |
|---|
| 1138 | 1194 | dso->long_name_allocated = name_allocated; |
|---|
| 1139 | 1195 | |
|---|
| 1140 | 1196 | if (root) |
|---|
| 1141 | | - __dso__findlink_by_longname(root, dso, NULL); |
|---|
| 1197 | + __dsos__findnew_link_by_longname_id(root, dso, NULL, id); |
|---|
| 1198 | +} |
|---|
| 1199 | + |
|---|
| 1200 | +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) |
|---|
| 1201 | +{ |
|---|
| 1202 | + dso__set_long_name_id(dso, name, NULL, name_allocated); |
|---|
| 1142 | 1203 | } |
|---|
| 1143 | 1204 | |
|---|
| 1144 | 1205 | void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated) |
|---|
| .. | .. |
|---|
| 1152 | 1213 | dso->short_name = name; |
|---|
| 1153 | 1214 | dso->short_name_len = strlen(name); |
|---|
| 1154 | 1215 | dso->short_name_allocated = name_allocated; |
|---|
| 1155 | | -} |
|---|
| 1156 | | - |
|---|
| 1157 | | -static void dso__set_basename(struct dso *dso) |
|---|
| 1158 | | -{ |
|---|
| 1159 | | - /* |
|---|
| 1160 | | - * basename() may modify path buffer, so we must pass |
|---|
| 1161 | | - * a copy. |
|---|
| 1162 | | - */ |
|---|
| 1163 | | - char *base, *lname = strdup(dso->long_name); |
|---|
| 1164 | | - |
|---|
| 1165 | | - if (!lname) |
|---|
| 1166 | | - return; |
|---|
| 1167 | | - |
|---|
| 1168 | | - /* |
|---|
| 1169 | | - * basename() may return a pointer to internal |
|---|
| 1170 | | - * storage which is reused in subsequent calls |
|---|
| 1171 | | - * so copy the result. |
|---|
| 1172 | | - */ |
|---|
| 1173 | | - base = strdup(basename(lname)); |
|---|
| 1174 | | - |
|---|
| 1175 | | - free(lname); |
|---|
| 1176 | | - |
|---|
| 1177 | | - if (!base) |
|---|
| 1178 | | - return; |
|---|
| 1179 | | - |
|---|
| 1180 | | - dso__set_short_name(dso, base, true); |
|---|
| 1181 | 1216 | } |
|---|
| 1182 | 1217 | |
|---|
| 1183 | 1218 | int dso__name_len(const struct dso *dso) |
|---|
| .. | .. |
|---|
| 1205 | 1240 | dso->sorted_by_name = true; |
|---|
| 1206 | 1241 | } |
|---|
| 1207 | 1242 | |
|---|
| 1208 | | -struct dso *dso__new(const char *name) |
|---|
| 1243 | +struct dso *dso__new_id(const char *name, struct dso_id *id) |
|---|
| 1209 | 1244 | { |
|---|
| 1210 | 1245 | struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); |
|---|
| 1211 | 1246 | |
|---|
| 1212 | 1247 | if (dso != NULL) { |
|---|
| 1213 | 1248 | strcpy(dso->name, name); |
|---|
| 1214 | | - dso__set_long_name(dso, dso->name, false); |
|---|
| 1249 | + if (id) |
|---|
| 1250 | + dso->id = *id; |
|---|
| 1251 | + dso__set_long_name_id(dso, dso->name, id, false); |
|---|
| 1215 | 1252 | dso__set_short_name(dso, dso->name, false); |
|---|
| 1216 | | - dso->symbols = dso->symbol_names = RB_ROOT; |
|---|
| 1253 | + dso->symbols = dso->symbol_names = RB_ROOT_CACHED; |
|---|
| 1217 | 1254 | dso->data.cache = RB_ROOT; |
|---|
| 1218 | | - dso->inlined_nodes = RB_ROOT; |
|---|
| 1219 | | - dso->srclines = RB_ROOT; |
|---|
| 1255 | + dso->inlined_nodes = RB_ROOT_CACHED; |
|---|
| 1256 | + dso->srclines = RB_ROOT_CACHED; |
|---|
| 1220 | 1257 | dso->data.fd = -1; |
|---|
| 1221 | 1258 | dso->data.status = DSO_DATA_STATUS_UNKNOWN; |
|---|
| 1222 | 1259 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; |
|---|
| .. | .. |
|---|
| 1228 | 1265 | dso->has_build_id = 0; |
|---|
| 1229 | 1266 | dso->has_srcline = 1; |
|---|
| 1230 | 1267 | dso->a2l_fails = 1; |
|---|
| 1231 | | - dso->kernel = DSO_TYPE_USER; |
|---|
| 1268 | + dso->kernel = DSO_SPACE__USER; |
|---|
| 1232 | 1269 | dso->needs_swap = DSO_SWAP__UNSET; |
|---|
| 1233 | 1270 | dso->comp = COMP_ID__NONE; |
|---|
| 1234 | 1271 | RB_CLEAR_NODE(&dso->rb_node); |
|---|
| .. | .. |
|---|
| 1240 | 1277 | } |
|---|
| 1241 | 1278 | |
|---|
| 1242 | 1279 | return dso; |
|---|
| 1280 | +} |
|---|
| 1281 | + |
|---|
| 1282 | +struct dso *dso__new(const char *name) |
|---|
| 1283 | +{ |
|---|
| 1284 | + return dso__new_id(name, NULL); |
|---|
| 1243 | 1285 | } |
|---|
| 1244 | 1286 | |
|---|
| 1245 | 1287 | void dso__delete(struct dso *dso) |
|---|
| .. | .. |
|---|
| 1286 | 1328 | dso__delete(dso); |
|---|
| 1287 | 1329 | } |
|---|
| 1288 | 1330 | |
|---|
| 1289 | | -void dso__set_build_id(struct dso *dso, void *build_id) |
|---|
| 1331 | +void dso__set_build_id(struct dso *dso, struct build_id *bid) |
|---|
| 1290 | 1332 | { |
|---|
| 1291 | | - memcpy(dso->build_id, build_id, sizeof(dso->build_id)); |
|---|
| 1333 | + dso->bid = *bid; |
|---|
| 1292 | 1334 | dso->has_build_id = 1; |
|---|
| 1293 | 1335 | } |
|---|
| 1294 | 1336 | |
|---|
| 1295 | | -bool dso__build_id_equal(const struct dso *dso, u8 *build_id) |
|---|
| 1337 | +bool dso__build_id_equal(const struct dso *dso, struct build_id *bid) |
|---|
| 1296 | 1338 | { |
|---|
| 1297 | | - return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; |
|---|
| 1339 | + if (dso->bid.size > bid->size && dso->bid.size == BUILD_ID_SIZE) { |
|---|
| 1340 | + /* |
|---|
| 1341 | + * For the backward compatibility, it allows a build-id has |
|---|
| 1342 | + * trailing zeros. |
|---|
| 1343 | + */ |
|---|
| 1344 | + return !memcmp(dso->bid.data, bid->data, bid->size) && |
|---|
| 1345 | + !memchr_inv(&dso->bid.data[bid->size], 0, |
|---|
| 1346 | + dso->bid.size - bid->size); |
|---|
| 1347 | + } |
|---|
| 1348 | + |
|---|
| 1349 | + return dso->bid.size == bid->size && |
|---|
| 1350 | + memcmp(dso->bid.data, bid->data, dso->bid.size) == 0; |
|---|
| 1298 | 1351 | } |
|---|
| 1299 | 1352 | |
|---|
| 1300 | 1353 | void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) |
|---|
| .. | .. |
|---|
| 1304 | 1357 | if (machine__is_default_guest(machine)) |
|---|
| 1305 | 1358 | return; |
|---|
| 1306 | 1359 | sprintf(path, "%s/sys/kernel/notes", machine->root_dir); |
|---|
| 1307 | | - if (sysfs__read_build_id(path, dso->build_id, |
|---|
| 1308 | | - sizeof(dso->build_id)) == 0) |
|---|
| 1360 | + if (sysfs__read_build_id(path, &dso->bid) == 0) |
|---|
| 1309 | 1361 | dso->has_build_id = true; |
|---|
| 1310 | 1362 | } |
|---|
| 1311 | 1363 | |
|---|
| .. | .. |
|---|
| 1323 | 1375 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", |
|---|
| 1324 | 1376 | root_dir, (int)strlen(name) - 1, name); |
|---|
| 1325 | 1377 | |
|---|
| 1326 | | - if (sysfs__read_build_id(filename, dso->build_id, |
|---|
| 1327 | | - sizeof(dso->build_id)) == 0) |
|---|
| 1378 | + if (sysfs__read_build_id(filename, &dso->bid) == 0) |
|---|
| 1328 | 1379 | dso->has_build_id = true; |
|---|
| 1329 | 1380 | |
|---|
| 1330 | 1381 | return 0; |
|---|
| 1331 | 1382 | } |
|---|
| 1332 | 1383 | |
|---|
| 1333 | | -bool __dsos__read_build_ids(struct list_head *head, bool with_hits) |
|---|
| 1334 | | -{ |
|---|
| 1335 | | - bool have_build_id = false; |
|---|
| 1336 | | - struct dso *pos; |
|---|
| 1337 | | - struct nscookie nsc; |
|---|
| 1338 | | - |
|---|
| 1339 | | - list_for_each_entry(pos, head, node) { |
|---|
| 1340 | | - if (with_hits && !pos->hit && !dso__is_vdso(pos)) |
|---|
| 1341 | | - continue; |
|---|
| 1342 | | - if (pos->has_build_id) { |
|---|
| 1343 | | - have_build_id = true; |
|---|
| 1344 | | - continue; |
|---|
| 1345 | | - } |
|---|
| 1346 | | - nsinfo__mountns_enter(pos->nsinfo, &nsc); |
|---|
| 1347 | | - if (filename__read_build_id(pos->long_name, pos->build_id, |
|---|
| 1348 | | - sizeof(pos->build_id)) > 0) { |
|---|
| 1349 | | - have_build_id = true; |
|---|
| 1350 | | - pos->has_build_id = true; |
|---|
| 1351 | | - } |
|---|
| 1352 | | - nsinfo__mountns_exit(&nsc); |
|---|
| 1353 | | - } |
|---|
| 1354 | | - |
|---|
| 1355 | | - return have_build_id; |
|---|
| 1356 | | -} |
|---|
| 1357 | | - |
|---|
| 1358 | | -void __dsos__add(struct dsos *dsos, struct dso *dso) |
|---|
| 1359 | | -{ |
|---|
| 1360 | | - list_add_tail(&dso->node, &dsos->head); |
|---|
| 1361 | | - __dso__findlink_by_longname(&dsos->root, dso, NULL); |
|---|
| 1362 | | - /* |
|---|
| 1363 | | - * It is now in the linked list, grab a reference, then garbage collect |
|---|
| 1364 | | - * this when needing memory, by looking at LRU dso instances in the |
|---|
| 1365 | | - * list with atomic_read(&dso->refcnt) == 1, i.e. no references |
|---|
| 1366 | | - * anywhere besides the one for the list, do, under a lock for the |
|---|
| 1367 | | - * list: remove it from the list, then a dso__put(), that probably will |
|---|
| 1368 | | - * be the last and will then call dso__delete(), end of life. |
|---|
| 1369 | | - * |
|---|
| 1370 | | - * That, or at the end of the 'struct machine' lifetime, when all |
|---|
| 1371 | | - * 'struct dso' instances will be removed from the list, in |
|---|
| 1372 | | - * dsos__exit(), if they have no other reference from some other data |
|---|
| 1373 | | - * structure. |
|---|
| 1374 | | - * |
|---|
| 1375 | | - * E.g.: after processing a 'perf.data' file and storing references |
|---|
| 1376 | | - * to objects instantiated while processing events, we will have |
|---|
| 1377 | | - * references to the 'thread', 'map', 'dso' structs all from 'struct |
|---|
| 1378 | | - * hist_entry' instances, but we may not need anything not referenced, |
|---|
| 1379 | | - * so we might as well call machines__exit()/machines__delete() and |
|---|
| 1380 | | - * garbage collect it. |
|---|
| 1381 | | - */ |
|---|
| 1382 | | - dso__get(dso); |
|---|
| 1383 | | -} |
|---|
| 1384 | | - |
|---|
| 1385 | | -void dsos__add(struct dsos *dsos, struct dso *dso) |
|---|
| 1386 | | -{ |
|---|
| 1387 | | - down_write(&dsos->lock); |
|---|
| 1388 | | - __dsos__add(dsos, dso); |
|---|
| 1389 | | - up_write(&dsos->lock); |
|---|
| 1390 | | -} |
|---|
| 1391 | | - |
|---|
| 1392 | | -struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short) |
|---|
| 1393 | | -{ |
|---|
| 1394 | | - struct dso *pos; |
|---|
| 1395 | | - |
|---|
| 1396 | | - if (cmp_short) { |
|---|
| 1397 | | - list_for_each_entry(pos, &dsos->head, node) |
|---|
| 1398 | | - if (strcmp(pos->short_name, name) == 0) |
|---|
| 1399 | | - return pos; |
|---|
| 1400 | | - return NULL; |
|---|
| 1401 | | - } |
|---|
| 1402 | | - return __dso__find_by_longname(&dsos->root, name); |
|---|
| 1403 | | -} |
|---|
| 1404 | | - |
|---|
| 1405 | | -struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short) |
|---|
| 1406 | | -{ |
|---|
| 1407 | | - struct dso *dso; |
|---|
| 1408 | | - down_read(&dsos->lock); |
|---|
| 1409 | | - dso = __dsos__find(dsos, name, cmp_short); |
|---|
| 1410 | | - up_read(&dsos->lock); |
|---|
| 1411 | | - return dso; |
|---|
| 1412 | | -} |
|---|
| 1413 | | - |
|---|
| 1414 | | -struct dso *__dsos__addnew(struct dsos *dsos, const char *name) |
|---|
| 1415 | | -{ |
|---|
| 1416 | | - struct dso *dso = dso__new(name); |
|---|
| 1417 | | - |
|---|
| 1418 | | - if (dso != NULL) { |
|---|
| 1419 | | - __dsos__add(dsos, dso); |
|---|
| 1420 | | - dso__set_basename(dso); |
|---|
| 1421 | | - /* Put dso here because __dsos_add already got it */ |
|---|
| 1422 | | - dso__put(dso); |
|---|
| 1423 | | - } |
|---|
| 1424 | | - return dso; |
|---|
| 1425 | | -} |
|---|
| 1426 | | - |
|---|
| 1427 | | -struct dso *__dsos__findnew(struct dsos *dsos, const char *name) |
|---|
| 1428 | | -{ |
|---|
| 1429 | | - struct dso *dso = __dsos__find(dsos, name, false); |
|---|
| 1430 | | - |
|---|
| 1431 | | - return dso ? dso : __dsos__addnew(dsos, name); |
|---|
| 1432 | | -} |
|---|
| 1433 | | - |
|---|
| 1434 | | -struct dso *dsos__findnew(struct dsos *dsos, const char *name) |
|---|
| 1435 | | -{ |
|---|
| 1436 | | - struct dso *dso; |
|---|
| 1437 | | - down_write(&dsos->lock); |
|---|
| 1438 | | - dso = dso__get(__dsos__findnew(dsos, name)); |
|---|
| 1439 | | - up_write(&dsos->lock); |
|---|
| 1440 | | - return dso; |
|---|
| 1441 | | -} |
|---|
| 1442 | | - |
|---|
| 1443 | | -size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
|---|
| 1444 | | - bool (skip)(struct dso *dso, int parm), int parm) |
|---|
| 1445 | | -{ |
|---|
| 1446 | | - struct dso *pos; |
|---|
| 1447 | | - size_t ret = 0; |
|---|
| 1448 | | - |
|---|
| 1449 | | - list_for_each_entry(pos, head, node) { |
|---|
| 1450 | | - if (skip && skip(pos, parm)) |
|---|
| 1451 | | - continue; |
|---|
| 1452 | | - ret += dso__fprintf_buildid(pos, fp); |
|---|
| 1453 | | - ret += fprintf(fp, " %s\n", pos->long_name); |
|---|
| 1454 | | - } |
|---|
| 1455 | | - return ret; |
|---|
| 1456 | | -} |
|---|
| 1457 | | - |
|---|
| 1458 | | -size_t __dsos__fprintf(struct list_head *head, FILE *fp) |
|---|
| 1459 | | -{ |
|---|
| 1460 | | - struct dso *pos; |
|---|
| 1461 | | - size_t ret = 0; |
|---|
| 1462 | | - |
|---|
| 1463 | | - list_for_each_entry(pos, head, node) { |
|---|
| 1464 | | - ret += dso__fprintf(pos, fp); |
|---|
| 1465 | | - } |
|---|
| 1466 | | - |
|---|
| 1467 | | - return ret; |
|---|
| 1468 | | -} |
|---|
| 1469 | | - |
|---|
| 1470 | | -size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) |
|---|
| 1384 | +static size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) |
|---|
| 1471 | 1385 | { |
|---|
| 1472 | 1386 | char sbuild_id[SBUILD_ID_SIZE]; |
|---|
| 1473 | 1387 | |
|---|
| 1474 | | - build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); |
|---|
| 1388 | + build_id__sprintf(&dso->bid, sbuild_id); |
|---|
| 1475 | 1389 | return fprintf(fp, "%s", sbuild_id); |
|---|
| 1476 | 1390 | } |
|---|
| 1477 | 1391 | |
|---|
| .. | .. |
|---|
| 1485 | 1399 | ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT "); |
|---|
| 1486 | 1400 | ret += dso__fprintf_buildid(dso, fp); |
|---|
| 1487 | 1401 | ret += fprintf(fp, ")\n"); |
|---|
| 1488 | | - for (nd = rb_first(&dso->symbols); nd; nd = rb_next(nd)) { |
|---|
| 1402 | + for (nd = rb_first_cached(&dso->symbols); nd; nd = rb_next(nd)) { |
|---|
| 1489 | 1403 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
|---|
| 1490 | 1404 | ret += symbol__fprintf(pos, fp); |
|---|
| 1491 | 1405 | } |
|---|