/* * report events to client * * This file is part of mtrace. * Copyright (C) 2015 Stefani Seibold * * This work was sponsored by Rohde & Schwarz GmbH & Co. KG, Munich. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include "backend.h" #include "backtrace.h" #include "common.h" #include "debug.h" #include "library.h" #include "memtrace.h" #include "options.h" #include "report.h" #include "server.h" #include "task.h" #include "trace.h" static int report_alloc64(struct task *task, enum mt_operation op, unsigned long ptr, unsigned long size, int depth) { int i = 0; struct mt_alloc_payload_64 *alloc = alloca(sizeof(*alloc) + depth * sizeof(uint64_t)); alloc->ptr = (uint64_t)ptr; alloc->size = (uint64_t)size; if (depth && backtrace_init_unwind(task) >= 0) { do { alloc->data[i] = (uint64_t)backtrace_get_ip(task); if (!alloc->data[i]) break; ++i; if (backtrace_step(task) < 0) break; } while(i < depth); } skip_breakpoint(task, task->event.e_un.breakpoint); return server_send_msg(op, task->leader->pid, task->pid, alloc, sizeof(*alloc) + i * sizeof(uint64_t)); } static int report_alloc32(struct task *task, enum mt_operation op, unsigned long ptr, unsigned long size, int depth) { int i = 0; struct mt_alloc_payload_32 *alloc = alloca(sizeof(*alloc) + depth * sizeof(uint32_t)); alloc->ptr = (uint32_t)ptr; alloc->size = (uint32_t)size; if (depth && backtrace_init_unwind(task) >= 0) { do { alloc->data[i] = (uint32_t)backtrace_get_ip(task); if (!alloc->data[i]) break; ++i; if (backtrace_step(task) < 0) break; } while(i < depth); } skip_breakpoint(task, task->event.e_un.breakpoint); return server_send_msg(op, task->leader->pid, task->pid, alloc, sizeof(*alloc) + i * sizeof(uint32_t)); } static int report_alloc(struct task *task, enum mt_operation op, unsigned long ptr, unsigned long size, int depth) { if (!ptr) return 0; if (!server_connected()) return -1; debug(DEBUG_FUNCTION, "%d [%d]: %#lx %lu", op, task->pid, ptr, size); if (task_is_64bit(task)) return report_alloc64(task, op, ptr, size, depth); else return report_alloc32(task, op, ptr, size, depth); } static int _null(struct task *task, struct library_symbol *libsym) { return 0; } static int _report_malloc(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long size = fetch_param(task, 0); unsigned long ret = fetch_retval(task); return report_alloc(task, MT_MALLOC, ret, size, options.bt_depth); } static int report_free(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long addr = fetch_param(task, 0); return report_alloc(task, MT_FREE, addr, 0, 0); } static int _report_realloc(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long addr = fetch_param(task, 0); unsigned long size = fetch_param(task, 1); unsigned long ret = fetch_retval(task); if (ret) return report_alloc(task, MT_REALLOC, ret, size, options.bt_depth); else return report_alloc(task, MT_REALLOC_FAILED, addr, 1, options.bt_depth); } static int report_realloc(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long addr = fetch_param(task, 0); unsigned long size = fetch_param(task, 1); return report_alloc(task, MT_REALLOC_ENTER, addr, size, options.bt_depth); } static int _report_calloc(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long size = fetch_param(task, 0) * fetch_param(task, 1); unsigned long ret = fetch_retval(task); return report_alloc(task, MT_MALLOC, ret, size, options.bt_depth); } static int _report_mmap(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long ret = fetch_retval(task); if ((void *)ret == MAP_FAILED) return 0; unsigned long size = fetch_param(task, 1); return report_alloc(task, MT_MMAP, ret, size, options.bt_depth); } static int _report_mmap64(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long ret = fetch_retval(task); if ((void *)ret == MAP_FAILED) return 0; union { uint64_t l; struct { uint32_t v1; uint32_t v2; } v; } size; size.l = fetch_param(task, 1); if (!task_is_64bit(task)) { size.v.v1 = fetch_param(task, 1); size.v.v2 = fetch_param(task, 2); } else size.l = fetch_param(task, 1); return report_alloc(task, MT_MMAP64, ret, size.l, options.bt_depth); } static int report_munmap(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long addr = fetch_param(task, 0); unsigned long size = fetch_param(task, 1); return report_alloc(task, MT_MUNMAP, addr, size, 0); } static int _report_memalign(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long size = fetch_param(task, 1); unsigned long ret = fetch_retval(task); return report_alloc(task, MT_MEMALIGN, ret, size, options.bt_depth); } static int _report_posix_memalign(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long ret = fetch_retval(task); if (ret) return 0; unsigned long size = fetch_param(task, 2); unsigned long ptr = fetch_param(task, 0); unsigned long new_ptr; if (task_is_64bit(task)) copy_from_proc(task, ARCH_ADDR_T(ptr), &new_ptr, sizeof(new_ptr)); else { uint32_t tmp; copy_from_proc(task, ARCH_ADDR_T(ptr), &tmp, sizeof(tmp)); new_ptr = tmp; } return report_alloc(task, MT_POSIX_MEMALIGN, new_ptr, size, options.bt_depth); } static int _report_aligned_alloc(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long size = fetch_param(task, 1); unsigned long ret = fetch_retval(task); return report_alloc(task, MT_ALIGNED_ALLOC, ret, size, options.bt_depth); } static int _report_valloc(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long size = fetch_param(task, 0); unsigned long ret = fetch_retval(task); return report_alloc(task, MT_VALLOC, ret, size, options.bt_depth); } static int _report_pvalloc(struct task *task, struct library_symbol *libsym) { if (!server_connected()) return -1; unsigned long size = fetch_param(task, 0); unsigned long ret = fetch_retval(task); return report_alloc(task, MT_PVALLOC, ret, size, options.bt_depth); } static int report_mremap(struct task *task, struct library_symbol *libsym) { unsigned long addr = fetch_param(task, 0); unsigned long size = fetch_param(task, 1); return report_alloc(task, MT_MUNMAP, addr, size, 0); } static int _report_mremap(struct task *task, struct library_symbol *libsym) { unsigned long size = fetch_param(task, 2); unsigned long ret = fetch_retval(task); return report_alloc(task, MT_MMAP, ret, size, options.bt_depth); } static const struct function flist[] = { { "malloc", 0, 2, NULL, _report_malloc }, { "free", 0, 3, report_free, NULL }, { "realloc", 0, 4, report_realloc, _report_realloc }, { "calloc", 0, 5, NULL, _report_calloc }, { "posix_memalign", 0, 6, NULL, _report_posix_memalign }, { "mmap", 0, 7, NULL, _report_mmap }, { "mmap64", 1, 8, NULL, _report_mmap64 }, { "munmap", 0, 9, report_munmap, _null }, { "memalign", 0, 10, NULL, _report_memalign }, { "aligned_alloc", 1, 11, NULL, _report_aligned_alloc }, { "valloc", 1, 12, NULL, _report_valloc }, { "pvalloc", 1, 13, NULL, _report_pvalloc }, { "mremap", 0, 14, report_mremap, _report_mremap}, { "cfree", 1, 15, report_free, NULL }, /* * support for Google gperftools * the c++ operators new and delete do not call malloc() and free() */ { "tc_delete", 1, 17, report_free, NULL }, { "tc_deletearray", 1, 18, report_free, NULL }, { "tc_deletearray_nothrow", 1, 19, report_free, NULL }, { "tc_delete_nothrow", 1, 20, report_free, NULL }, { "tc_new", 1, 21, NULL, _report_malloc }, { "tc_newarray", 1, 22, NULL, _report_malloc }, { "tc_newarray_nothrow", 1, 23, NULL, _report_malloc }, { "tc_new_nothrow", 1, 24, NULL, _report_malloc }, }; const struct function *flist_matches_symbol(const char *sym_name) { unsigned int i; for(i = 0; i < ARRAY_SIZE(flist); ++i) { if (!strcmp(sym_name, flist[i].name)) return &flist[i]; } return 0; } int _report_map(struct task *task, struct library *lib, enum mt_operation op) { size_t len = strlen(lib->filename) + 1; struct mt_map_payload *payload = alloca(sizeof(struct mt_map_payload) + len); payload->addr = lib->load_addr; payload->offset = lib->load_offset; payload->size = lib->load_size; memcpy(payload->filename, lib->filename, len); return server_send_msg(op, task->pid, 0, payload, sizeof(struct mt_map_payload) + len); } int report_add_map(struct task *task, struct library *lib) { if (!server_connected()) return -1; return _report_map(task, lib, MT_ADD_MAP); } int report_del_map(struct task *task, struct library *lib) { if (!server_connected()) return -1; return _report_map(task, lib, MT_DEL_MAP); } int report_info(int do_trace) { struct memtrace_info mt_info; if (!server_connected()) return -1; mt_info.version = MEMTRACE_SI_VERSION; mt_info.mode = 0; mt_info.do_trace = do_trace ? 1 : 0; mt_info.stack_depth = options.bt_depth; if (options.verbose) mt_info.mode |= MEMTRACE_SI_VERBOSE; if (options.follow_exec) mt_info.mode |= MEMTRACE_SI_EXEC; if (options.follow) mt_info.mode |= MEMTRACE_SI_FORK; return server_send_msg(MT_INFO, 0, 0, &mt_info, sizeof(mt_info)); } int report_scan(pid_t pid, const void *data, unsigned int data_len) { if (!server_connected()) return -1; return server_send_msg(MT_SCAN, pid, 0, data, data_len); } int report_attach(struct task *task) { struct mt_attached_payload state = { .attached = task->attached }; if (!server_connected()) return -1; return server_send_msg(task_is_64bit(task) ? MT_ATTACH64 : MT_ATTACH, task->pid, 0, &state, sizeof(state)); } int report_fork(struct task *task, struct task *ptask) { struct mt_pid_payload fork_pid = { .pid = ptask->leader->pid }; if (!server_connected()) return -1; return server_send_msg(MT_FORK, task->pid, 0, &fork_pid, sizeof(fork_pid)); } int report_exit(struct task *task) { if (!server_connected()) return -1; return server_send_msg(MT_EXIT, task->pid, 0, NULL, 0); } int report_about_exit(struct task *task) { if (!server_connected()) return -1; return server_send_msg(MT_ABOUT_EXIT, task->pid, 0, NULL, 0); } int report_nofollow(struct task *task) { if (!server_connected()) return -1; return server_send_msg(MT_NOFOLLOW, task->pid, 0, NULL, 0); } int report_detach(struct task *task) { if (!server_connected()) return -1; return server_send_msg(MT_DETACH, task->pid, 0, NULL, 0); } int report_disconnect(void) { if (!server_connected()) return -1; return server_send_msg(MT_DISCONNECT, 0, 0, NULL, 0); } static void report_process(struct task *leader) { struct list_head *it; report_attach(leader); list_for_each(it, &leader->libraries_list) { struct library *lib = container_of(it, struct library, list); report_add_map(leader, lib); } } int report_processes(void) { if (!server_connected()) return -1; each_process(&report_process); return 0; }