diff options
Diffstat (limited to 'tools/perf/util')
45 files changed, 1959 insertions, 2899 deletions
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 95264f30417..6aa34e5afdc 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -9,18 +9,14 @@ GVF=${OUTPUT}PERF-VERSION-FILE LF=' ' +# # First check if there is a .git to get the version from git describe -# otherwise try to get the version from the kernel makefile +# otherwise try to get the version from the kernel Makefile +# if test -d ../../.git -o -f ../../.git && - VN=$(git describe --match 'v[0-9].[0-9]*' --abbrev=4 HEAD 2>/dev/null) && - case "$VN" in - *$LF*) (exit 1) ;; - v[0-9]*) - git update-index -q --refresh - test -z "$(git diff-index --name-only HEAD --)" || - VN="$VN-dirty" ;; - esac + VN=$(git tag 2>/dev/null | tail -1 | grep -E "v[0-9].[0-9]*") then + VN=$(echo $VN"-g"$(git log -1 --abbrev=4 --pretty=format:"%h" HEAD)) VN=$(echo "$VN" | sed -e 's/-/./g'); else VN=$(MAKEFLAGS= make -sC ../.. kernelversion) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index f0a91037137..07aaeea6000 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -15,6 +15,7 @@ #include "debug.h" #include "annotate.h" #include <pthread.h> +#include <linux/bitops.h> const char *disassembler_style; const char *objdump_path; @@ -170,15 +171,15 @@ static int lock__parse(struct ins_operands *ops) if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) goto out_free_ops; - ops->locked.ins = ins__find(name); - if (ops->locked.ins == NULL) - goto out_free_ops; + ops->locked.ins = ins__find(name); + if (ops->locked.ins == NULL) + goto out_free_ops; - if (!ops->locked.ins->ops) - return 0; + if (!ops->locked.ins->ops) + return 0; - if (ops->locked.ins->ops->parse) - ops->locked.ins->ops->parse(ops->locked.ops); + if (ops->locked.ins->ops->parse) + ops->locked.ins->ops->parse(ops->locked.ops); return 0; @@ -400,6 +401,8 @@ static struct ins instructions[] = { { .name = "testb", .ops = &mov_ops, }, { .name = "testl", .ops = &mov_ops, }, { .name = "xadd", .ops = &mov_ops, }, + { .name = "xbeginl", .ops = &jump_ops, }, + { .name = "xbeginq", .ops = &jump_ops, }, }; static int ins__cmp(const void *name, const void *insp) @@ -855,21 +858,68 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin struct source_line *iter; struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; + int ret; while (*p != NULL) { parent = *p; iter = rb_entry(parent, struct source_line, node); - if (src_line->percent > iter->percent) + ret = strcmp(iter->path, src_line->path); + if (ret == 0) { + iter->percent_sum += src_line->percent; + return; + } + + if (ret < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } + src_line->percent_sum = src_line->percent; + rb_link_node(&src_line->node, parent, p); rb_insert_color(&src_line->node, root); } +static void __resort_source_line(struct rb_root *root, struct source_line *src_line) +{ + struct source_line *iter; + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + + while (*p != NULL) { + parent = *p; + iter = rb_entry(parent, struct source_line, node); + + if (src_line->percent_sum > iter->percent_sum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&src_line->node, parent, p); + rb_insert_color(&src_line->node, root); +} + +static void resort_source_line(struct rb_root *dest_root, struct rb_root *src_root) +{ + struct source_line *src_line; + struct rb_node *node; + + node = rb_first(src_root); + while (node) { + struct rb_node *next; + + src_line = rb_entry(node, struct source_line, node); + next = rb_next(node); + rb_erase(node, src_root); + + __resort_source_line(dest_root, src_line); + node = next; + } +} + static void symbol__free_source_line(struct symbol *sym, int len) { struct annotation *notes = symbol__annotation(sym); @@ -894,6 +944,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, struct source_line *src_line; struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); + struct rb_root tmp_root = RB_ROOT; if (!h->sum) return 0; @@ -928,12 +979,13 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, goto next; strcpy(src_line[i].path, path); - insert_source_line(root, &src_line[i]); + insert_source_line(&tmp_root, &src_line[i]); next: pclose(fp); } + resort_source_line(root, &tmp_root); return 0; } @@ -957,7 +1009,7 @@ static void print_summary(struct rb_root *root, const char *filename) char *path; src_line = rb_entry(node, struct source_line, node); - percent = src_line->percent; + percent = src_line->percent_sum; color = get_percent_color(percent); path = src_line->path; diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 39242dcee8f..8eec94358a4 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -5,6 +5,7 @@ #include <stdint.h> #include "types.h" #include "symbol.h" +#include "hist.h" #include <linux/list.h> #include <linux/rbtree.h> #include <pthread.h> @@ -75,6 +76,7 @@ struct sym_hist { struct source_line { struct rb_node node; double percent; + double percent_sum; char *path; }; @@ -140,20 +142,18 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, #ifdef NEWT_SUPPORT int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, - void(*timer)(void *arg), void *arg, int delay_secs); + struct hist_browser_timer *hbt); #else static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, struct map *map __maybe_unused, int evidx __maybe_unused, - void(*timer)(void *arg) __maybe_unused, - void *arg __maybe_unused, - int delay_secs __maybe_unused) + struct hist_browser_timer *hbt + __maybe_unused) { return 0; } #endif extern const char *disassembler_style; -extern const char *objdump_path; #endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 8e3a740ddbd..5295625c0c0 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -16,11 +16,11 @@ #include "session.h" #include "tool.h" -static int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, - union perf_event *event, - struct perf_sample *sample __maybe_unused, - struct perf_evsel *evsel __maybe_unused, - struct machine *machine) +int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct perf_evsel *evsel __maybe_unused, + struct machine *machine) { struct addr_location al; u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -64,12 +64,27 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, struct perf_tool build_id__mark_dso_hit_ops = { .sample = build_id__mark_dso_hit, .mmap = perf_event__process_mmap, - .fork = perf_event__process_task, + .fork = perf_event__process_fork, .exit = perf_event__exit_del_thread, .attr = perf_event__process_attr, .build_id = perf_event__process_build_id, }; +int build_id__sprintf(const u8 *build_id, int len, char *bf) +{ + char *bid = bf; + const u8 *raw = build_id; + int i; + + for (i = 0; i < len; ++i) { + sprintf(bid, "%02x", *raw); + ++raw; + bid += 2; + } + + return raw - build_id; +} + char *dso__build_id_filename(struct dso *self, char *bf, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a993ba87d99..a811f5c62e1 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -1,10 +1,19 @@ #ifndef PERF_BUILD_ID_H_ #define PERF_BUILD_ID_H_ 1 -#include "session.h" +#define BUILD_ID_SIZE 20 + +#include "tool.h" +#include "types.h" extern struct perf_tool build_id__mark_dso_hit_ops; +struct dso; +int build_id__sprintf(const u8 *build_id, int len, char *bf); char *dso__build_id_filename(struct dso *self, char *bf, size_t size); +int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, + struct perf_sample *sample, struct perf_evsel *evsel, + struct machine *machine); + #endif diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 2bd51370ad2..26e36723987 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -5,6 +5,7 @@ #include "util.h" #include "strbuf.h" #include "../perf.h" +#include "../ui/ui.h" #define CMD_EXEC_PATH "--exec-path" #define CMD_PERF_DIR "--perf-dir=" @@ -31,44 +32,6 @@ extern const char *pager_program; extern int pager_in_use(void); extern int pager_use_color; -extern int use_browser; - -#if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) -void setup_browser(bool fallback_to_pager); -void exit_browser(bool wait_for_ok); - -#ifdef NEWT_SUPPORT -int ui__init(void); -void ui__exit(bool wait_for_ok); -#else -static inline int ui__init(void) -{ - return -1; -} -static inline void ui__exit(bool wait_for_ok __maybe_unused) {} -#endif - -#ifdef GTK2_SUPPORT -int perf_gtk__init(void); -void perf_gtk__exit(bool wait_for_ok); -#else -static inline int perf_gtk__init(void) -{ - return -1; -} -static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} -#endif - -#else /* NEWT_SUPPORT || GTK2_SUPPORT */ - -static inline void setup_browser(bool fallback_to_pager) -{ - if (fallback_to_pager) - setup_pager(); -} -static inline void exit_browser(bool wait_for_ok __maybe_unused) {} -#endif /* NEWT_SUPPORT || GTK2_SUPPORT */ - char *alias_lookup(const char *alias); int split_cmdline(char *cmdline, const char ***argv); diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index dec98750b48..83e8d234af6 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -26,6 +26,7 @@ int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); static inline void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused, const char *title __maybe_unused) {} +static inline void ui_progress__finish(void) {} #define ui__error(format, arg...) ui__warning(format, ##arg) diff --git a/tools/perf/util/dso-test-data.c b/tools/perf/util/dso-test-data.c deleted file mode 100644 index c6caedeb1d6..00000000000 --- a/tools/perf/util/dso-test-data.c +++ /dev/null @@ -1,153 +0,0 @@ -#include "util.h" - -#include <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <string.h> - -#include "symbol.h" - -#define TEST_ASSERT_VAL(text, cond) \ -do { \ - if (!(cond)) { \ - pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ - return -1; \ - } \ -} while (0) - -static char *test_file(int size) -{ - static char buf_templ[] = "/tmp/test-XXXXXX"; - char *templ = buf_templ; - int fd, i; - unsigned char *buf; - - fd = mkstemp(templ); - - buf = malloc(size); - if (!buf) { - close(fd); - return NULL; - } - - for (i = 0; i < size; i++) - buf[i] = (unsigned char) ((int) i % 10); - - if (size != write(fd, buf, size)) - templ = NULL; - - close(fd); - return templ; -} - -#define TEST_FILE_SIZE (DSO__DATA_CACHE_SIZE * 20) - -struct test_data_offset { - off_t offset; - u8 data[10]; - int size; -}; - -struct test_data_offset offsets[] = { - /* Fill first cache page. */ - { - .offset = 10, - .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, - .size = 10, - }, - /* Read first cache page. */ - { - .offset = 10, - .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, - .size = 10, - }, - /* Fill cache boundary pages. */ - { - .offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10, - .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, - .size = 10, - }, - /* Read cache boundary pages. */ - { - .offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10, - .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, - .size = 10, - }, - /* Fill final cache page. */ - { - .offset = TEST_FILE_SIZE - 10, - .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, - .size = 10, - }, - /* Read final cache page. */ - { - .offset = TEST_FILE_SIZE - 10, - .data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, - .size = 10, - }, - /* Read final cache page. */ - { - .offset = TEST_FILE_SIZE - 3, - .data = { 7, 8, 9, 0, 0, 0, 0, 0, 0, 0 }, - .size = 3, - }, -}; - -int dso__test_data(void) -{ - struct machine machine; - struct dso *dso; - char *file = test_file(TEST_FILE_SIZE); - size_t i; - - TEST_ASSERT_VAL("No test file", file); - - memset(&machine, 0, sizeof(machine)); - - dso = dso__new((const char *)file); - - /* Basic 10 bytes tests. */ - for (i = 0; i < ARRAY_SIZE(offsets); i++) { - struct test_data_offset *data = &offsets[i]; - ssize_t size; - u8 buf[10]; - - memset(buf, 0, 10); - size = dso__data_read_offset(dso, &machine, data->offset, - buf, 10); - - TEST_ASSERT_VAL("Wrong size", size == data->size); - TEST_ASSERT_VAL("Wrong data", !memcmp(buf, data->data, 10)); - } - - /* Read cross multiple cache pages. */ - { - ssize_t size; - int c; - u8 *buf; - - buf = malloc(TEST_FILE_SIZE); - TEST_ASSERT_VAL("ENOMEM\n", buf); - - /* First iteration to fill caches, second one to read them. */ - for (c = 0; c < 2; c++) { - memset(buf, 0, TEST_FILE_SIZE); - size = dso__data_read_offset(dso, &machine, 10, - buf, TEST_FILE_SIZE); - - TEST_ASSERT_VAL("Wrong size", - size == (TEST_FILE_SIZE - 10)); - - for (i = 0; i < (size_t)size; i++) - TEST_ASSERT_VAL("Wrong data", - buf[i] == (i % 10)); - } - - free(buf); - } - - dso__delete(dso); - unlink(file); - return 0; -} diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c new file mode 100644 index 00000000000..d6d9a465acd --- /dev/null +++ b/tools/perf/util/dso.c @@ -0,0 +1,595 @@ +#include "symbol.h" +#include "dso.h" +#include "machine.h" +#include "util.h" +#include "debug.h" + +char dso__symtab_origin(const struct dso *dso) +{ + static const char origin[] = { + [DSO_BINARY_TYPE__KALLSYMS] = 'k', + [DSO_BINARY_TYPE__VMLINUX] = 'v', + [DSO_BINARY_TYPE__JAVA_JIT] = 'j', + [DSO_BINARY_TYPE__DEBUGLINK] = 'l', + [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', + [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', + [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', + [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', + [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', + [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', + [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', + [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', + [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', + }; + + if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) + return '!'; + return origin[dso->symtab_type]; +} + +int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, + char *root_dir, char *file, size_t size) +{ + char build_id_hex[BUILD_ID_SIZE * 2 + 1]; + int ret = 0; + + switch (type) { + case DSO_BINARY_TYPE__DEBUGLINK: { + char *debuglink; + + strncpy(file, dso->long_name, size); + debuglink = file + dso->long_name_len; + while (debuglink != file && *debuglink != '/') + debuglink--; + if (*debuglink == '/') + debuglink++; + filename__read_debuglink(dso->long_name, debuglink, + size - (debuglink - file)); + } + break; + case DSO_BINARY_TYPE__BUILD_ID_CACHE: + /* skip the locally configured cache if a symfs is given */ + if (symbol_conf.symfs[0] || + (dso__build_id_filename(dso, file, size) == NULL)) + ret = -1; + break; + + case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: + snprintf(file, size, "%s/usr/lib/debug%s.debug", + symbol_conf.symfs, dso->long_name); + break; + + case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: + snprintf(file, size, "%s/usr/lib/debug%s", + symbol_conf.symfs, dso->long_name); + break; + + case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: + if (!dso->has_build_id) { + ret = -1; + break; + } + + build_id__sprintf(dso->build_id, + sizeof(dso->build_id), + build_id_hex); + snprintf(file, size, + "%s/usr/lib/debug/.build-id/%.2s/%s.debug", + symbol_conf.symfs, build_id_hex, build_id_hex + 2); + break; + + case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: + snprintf(file, size, "%s%s", + symbol_conf.symfs, dso->long_name); + break; + + case DSO_BINARY_TYPE__GUEST_KMODULE: + snprintf(file, size, "%s%s%s", symbol_conf.symfs, + root_dir, dso->long_name); + break; + + case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: + snprintf(file, size, "%s%s", symbol_conf.symfs, + dso->long_name); + break; + + default: + case DSO_BINARY_TYPE__KALLSYMS: + case DSO_BINARY_TYPE__VMLINUX: + case DSO_BINARY_TYPE__GUEST_KALLSYMS: + case DSO_BINARY_TYPE__GUEST_VMLINUX: + case DSO_BINARY_TYPE__JAVA_JIT: + case DSO_BINARY_TYPE__NOT_FOUND: + ret = -1; + break; + } + + return ret; +} + +static int open_dso(struct dso *dso, struct machine *machine) +{ + char *root_dir = (char *) ""; + char *name; + int fd; + + name = malloc(PATH_MAX); + if (!name) + return -ENOMEM; + + if (machine) + root_dir = machine->root_dir; + + if (dso__binary_type_file(dso, dso->data_type, + root_dir, name, PATH_MAX)) { + free(name); + return -EINVAL; + } + + fd = open(name, O_RDONLY); + free(name); + return fd; +} + +int dso__data_fd(struct dso *dso, struct machine *machine) +{ + static enum dso_binary_type binary_type_data[] = { + DSO_BINARY_TYPE__BUILD_ID_CACHE, + DSO_BINARY_TYPE__SYSTEM_PATH_DSO, + DSO_BINARY_TYPE__NOT_FOUND, + }; + int i = 0; + + if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) + return open_dso(dso, machine); + + do { + int fd; + + dso->data_type = binary_type_data[i++]; + + fd = open_dso(dso, machine); + if (fd >= 0) + return fd; + + } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); + + return -EINVAL; +} + +static void +dso_cache__free(struct rb_root *root) +{ + struct rb_node *next = rb_first(root); + + while (next) { + struct dso_cache *cache; + + cache = rb_entry(next, struct dso_cache, rb_node); + next = rb_next(&cache->rb_node); + rb_erase(&cache->rb_node, root); + free(cache); + } +} + +static struct dso_cache* +dso_cache__find(struct rb_root *root, u64 offset) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct dso_cache *cache; + + while (*p != NULL) { + u64 end; + + parent = *p; + cache = rb_entry(parent, struct dso_cache, rb_node); + end = cache->offset + DSO__DATA_CACHE_SIZE; + + if (offset < cache->offset) + p = &(*p)->rb_left; + else if (offset >= end) + p = &(*p)->rb_right; + else + return cache; + } + return NULL; +} + +static void +dso_cache__insert(struct rb_root *root, struct dso_cache *new) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct dso_cache *cache; + u64 offset = new->offset; + + while (*p != NULL) { + u64 end; + + parent = *p; + cache = rb_entry(parent, struct dso_cache, rb_node); + end = cache->offset + DSO__DATA_CACHE_SIZE; + + if (offset < cache->offset) + p = &(*p)->rb_left; + else if (offset >= end) + p = &(*p)->rb_right; + } + + rb_link_node(&new->rb_node, parent, p); + rb_insert_color(&new->rb_node, root); +} + +static ssize_t +dso_cache__memcpy(struct dso_cache *cache, u64 offset, + u8 *data, u64 size) +{ + u64 cache_offset = offset - cache->offset; + u64 cache_size = min(cache->size - cache_offset, size); + + memcpy(data, cache->data + cache_offset, cache_size); + return cache_size; +} + +static ssize_t +dso_cache__read(struct dso *dso, struct machine *machine, + u64 offset, u8 *data, ssize_t size) +{ + struct dso_cache *cache; + ssize_t ret; + int fd; + + fd = dso__data_fd(dso, machine); + if (fd < 0) + return -1; + + do { + u64 cache_offset; + + ret = -ENOMEM; + + cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); + if (!cache) + break; + + cache_offset = offset & DSO__DATA_CACHE_MASK; + ret = -EINVAL; + + if (-1 == lseek(fd, cache_offset, SEEK_SET)) + break; + + ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); + if (ret <= 0) + break; + + cache->offset = cache_offset; + cache->size = ret; + dso_cache__insert(&dso->cache, cache); + + ret = dso_cache__memcpy(cache, offset, data, size); + + } while (0); + + if (ret <= 0) + free(cache); + + close(fd); + return ret; +} + +static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, + u64 offset, u8 *data, ssize_t size) +{ + struct dso_cache *cache; + + cache = dso_cache__find(&dso->cache, offset); + if (cache) + return dso_cache__memcpy(cache, offset, data, size); + else + return dso_cache__read(dso, machine, offset, data, size); +} + +ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, + u64 offset, u8 *data, ssize_t size) +{ + ssize_t r = 0; + u8 *p = data; + + do { + ssize_t ret; + + ret = dso_cache_read(dso, machine, offset, p, size); + if (ret < 0) + return ret; + + /* Reached EOF, return what we have. */ + if (!ret) + break; + + BUG_ON(ret > size); + + r += ret; + p += ret; + offset += ret; + size -= ret; + + } while (size); |