From 2b1dde636396ae748053447dc99dadb9e3a629a6 Mon Sep 17 00:00:00 2001 From: Maxim Ostapenko Date: Tue, 27 Sep 2016 18:21:23 +0300 Subject: [PATCH] Extract information about leaked chunks from da_swap_lsan.so. Since we want to send information about leaked chunks to host, we need somehow extract it from liblsan.so. LSan provides couple of hooks to achieve this: - __lsan_get_leaked_objects_num: returns the number of leaked objects. - __lsan_get_leaked_objects_ptr: returns a pointer to array of leaked objects. Just use these hooks to extract necessary information. When extraction is completed, notify the host about this. Change-Id: I8e779368ab440ab02a9ef0db753621720f27ec61 Signed-off-by: Maxim Ostapenko --- helper/lsan_open.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++---- include/binproto.h | 2 + 2 files changed, 113 insertions(+), 9 deletions(-) diff --git a/helper/lsan_open.c b/helper/lsan_open.c index 6b098a8..81a6180 100644 --- a/helper/lsan_open.c +++ b/helper/lsan_open.c @@ -28,6 +28,7 @@ #include #include #include "lsan_open.h" +#include "binproto.h" #include "daprobe.h" #include "dahelper.h" #include "lsan/lsan_interface.h" @@ -35,17 +36,67 @@ static void *liblsan_handle; int lsan_init_succeeded; +enum lsan_status { + LSAN_MSG_ERR = 0x0, + LSAN_MSG_STATUS = 0x1, + LSAN_MSG_REPORT = 0x2, + LSAN_INIT_DONE = 0x4 +}; + +typedef enum lsan_status lsan_status; +typedef int (*lsan_int_fun)(); +typedef const void *(*lsan_const_void_star_fun)(); +typedef struct LeakedObject LeakedObject; + +static void send_msg_to_host(const char *msg, lsan_status status) +{ + char *msg_buf = (char *)malloc(MAX_LOCAL_BUF_SIZE); + char *p = msg_buf; + /* Pack common header. */ + p = pack_int32(p, MSG_LSAN); + p = pack_int32(p, 0); + p = pack_timestamp(p); + p = pack_int32(p, 0); + /* Pack LSan specific data. */ + p = pack_int32(p, status); + p = pack_int32(p, _getpid()); + p = pack_args(p, "s", msg); + /* Need this crap to make WRITE_MSG happy. */ + char *call_type_ptr = p; + p = pack_int32(p, (uint32_t)0); /* internal call*/ + char *caller_ptr = p; + p = pack_int64(p, (uintptr_t)0); /*caller addr*/ + + SET_MSG_LEN(); + /* Need to provide third, fourth and fifth arguments. P seems to be OK. + */ + WRITE_MSG(msg_buf, (p - msg_buf), call_type_ptr, caller_ptr, 0); + free(msg_buf); + msg_buf = NULL; +} + +static char *get_report_file_name() { + const char report_file_name_base[] = "/tmp/lsan.log"; + static char report_file_name[MAX_PATH_LENGTH]; + snprintf(report_file_name, MAX_PATH_LENGTH, "%s.%d", + report_file_name_base, _getpid()); + return report_file_name; +} + int lsan_open_liblsan() { if (!isOptionEnabled(OPT_ALLOC_ALWAYS) || !isOptionEnabled(OPT_FL_LSAN)) return 0; liblsan_handle = dlopen("/usr/lib/da_swap_lsan.so", RTLD_NOW); - if (liblsan_handle != NULL) + if (liblsan_handle != NULL) { lsan_init_succeeded = 1; - else + send_msg_to_host("da_swap_lsan.so init done!\n", + LSAN_MSG_STATUS | LSAN_INIT_DONE); + } else { PRINTERR("Cannot open da_swap_lsan.so due to reason: %s!\n", dlerror()); + } return liblsan_handle != NULL; } @@ -55,15 +106,65 @@ void lsan_close_liblsan() dlclose(liblsan_handle); } +static int lsan_call_int_cb(const char *func_name) +{ + int result = -1; + lsan_int_fun callback = (lsan_int_fun)lsan_get_sym(func_name); + if (callback) { + PRINTMSG("Calling lsan callback %s\n", func_name); + result = callback(); + } + return result; +} + +static void send_leaked_objects_to_host(int leaked_objects_num, + LeakedObject *leaked_objects) +{ + int i; + unsigned long res = 0; + const char *report_file_name = get_report_file_name(); + FILE *report_file = fopen(report_file_name, "w+"); + if (!report_file) { + PRINTERR( + "Cannot open tmp file for LSan report due to reason: %s\n!", + strerror(errno)); + send_msg_to_host("Failed to open LSan report file!\n", + LSAN_MSG_ERR); + return; + } + for (i = 0; i < leaked_objects_num; ++i) { + res += leaked_objects[i].size; + fprintf(report_file, "%lu %lu %u\n", leaked_objects[i].addr, + leaked_objects[i].size, + leaked_objects[i].first_four_bytes); + } + fclose(report_file); + send_msg_to_host(report_file_name, LSAN_MSG_REPORT); +} + +static void report_leaked_chunks(int leaked_objects_num) +{ + lsan_const_void_star_fun func = (lsan_const_void_star_fun)lsan_get_sym( + "__lsan_get_leaked_objects_ptr"); + if (func) { + LeakedObject *leaked_objects = (LeakedObject *)func(); + send_leaked_objects_to_host(leaked_objects_num, leaked_objects); + } +} + void lsan_do_leak_check() { if (!liblsan_handle) return; - int (*__lsan_check_cb)() = - lsan_get_sym("__lsan_do_recoverable_leak_check"); - if (__lsan_check_cb) { - PRINTMSG("Calling __lsan_check_cb \n"); - __lsan_check_cb(); + lsan_call_int_cb("__lsan_do_recoverable_leak_check"); + int leaked_objects_num = + lsan_call_int_cb("__lsan_get_leaked_objects_num"); + if (leaked_objects_num != -1) { + report_leaked_chunks(leaked_objects_num); + } else { + send_msg_to_host("Error occurred during " + "__lsan_get_leaked_objects_num!\n", + LSAN_MSG_ERR); } } @@ -76,11 +177,12 @@ void *lsan_get_sym(const char *symbol_name) return NULL; } void *sym = dlsym(liblsan_handle, symbol_name); - if (!sym) + if (!sym) { PRINTERR("Failed to get symbol %s from liblsan.so due to " "reason %s!\n", symbol_name, dlerror()); - else + } else { PRINTMSG("Loaded symbol %s at address %p\n", symbol_name, sym); + } return sym; } diff --git a/include/binproto.h b/include/binproto.h index 46caf8a..1f6976d 100644 --- a/include/binproto.h +++ b/include/binproto.h @@ -61,6 +61,8 @@ #define MSG_PROBE_NETWORK 0x0111 #define MSG_PROBE_GL 0x0112 +#define MSG_LSAN 0x0022 + #define SCREENSHOT_DIRECTORY "/tmp/da" #define MAX_PACK_FILENAME_LEN (4 * 1024) -- 2.7.4