From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755208Ab3LUPth (ORCPT ); Sat, 21 Dec 2013 10:49:37 -0500 Received: from mail-pb0-f54.google.com ([209.85.160.54]:50924 "EHLO mail-pb0-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753143Ab3LUPtf (ORCPT ); Sat, 21 Dec 2013 10:49:35 -0500 Message-ID: <52B5B885.2070008@gmail.com> Date: Sat, 21 Dec 2013 23:49:25 +0800 From: Hui Zhu User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.2.0 MIME-Version: 1.0 To: "linux-kernel@vger.kernel.org" CC: acme@redhat.com, bp@suse.de, gthelen@google.com, rui.zhang@intel.com, jacob.jun.pan@linux.intel.com, albcamus@gmail.com Subject: [PATCH 1/2] tool and script help GDB load Linux kernel modules (tool) Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org getmod a application of KGTP (https://kgtp.googlecode.com). It can get Linux Kernel modules info from sysfs and output them as GDB command add-symbol-file format. Then you can call "source output_file_name" inside GDB to let it load all the debug info of Linux Kernel modules. It will help debug Linux Kernel modules with GDB with gdbstub such as KGTP, KGDB, QEMU. It was written by C and built with -static make it friendly to the embedded system. Following part is the help info of getmod: Usage: ./getmod [option] -s dir Add dir to module search directory list. This options can use more than once. -S Add /lib/modules/3.2.0-57-generic/kernel to module search directory list. -r dir Add dir to replace the directory. This options can use more than once. -n No search the directory of the module file directory. -h Display this information. Signed-off-by: Hui Zhu --- --- a/tools/Makefile +++ b/tools/Makefile @@ -16,6 +16,7 @@ help: @echo ' vm - misc vm tools' @echo ' x86_energy_perf_policy - Intel energy policy tool' @echo ' tmon - thermal monitoring and tuning tool' + @echo ' getmod - Output LKM info in GDB add-symbol-file format.' @echo '' @echo 'You can do:' @echo ' $$ make -C tools/ _install' @@ -36,7 +37,7 @@ help: cpupower: FORCE $(call descend,power/$@) -cgroup firewire guest usb virtio vm net: FORCE +cgroup firewire guest usb virtio vm net getmod: FORCE $(call descend,$@) liblk: FORCE @@ -77,7 +78,7 @@ install: cgroup_install cpupower_install cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean: +cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean getmod_clean: $(call descend,$(@:_clean=),clean) liblk_clean: @@ -97,6 +98,7 @@ tmon_clean: clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \ selftests_clean turbostat_clean usb_clean virtio_clean \ - vm_clean net_clean x86_energy_perf_policy_clean tmon_clean + vm_clean net_clean x86_energy_perf_policy_clean tmon_clean \ + getmod_clean .PHONY: FORCE --- /dev/null +++ b/tools/getmod/Makefile @@ -0,0 +1,11 @@ +prefix = /usr + +all : getmod + +getmod : CFLAGS = -Wall -O2 -static + +clean : + rm -rf getmod + +install : + install getmod $(prefix)/sbin/ --- /dev/null +++ b/tools/getmod/getmod.c @@ -0,0 +1,407 @@ +/* + * Output Linux Kernel modules info in GDB add-symbol-file format. + * + * Copyright(C) KGTP team (https://kgtp.googlecode.com), 2011-2013 + * Licensed under the GNU General Public License, version 2.0 (GPLv2) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOD_DIR "/lib/modules" +#define PROC_MOD "/proc/modules" +#define SYS_MOD "/sys/module" + +#define SDIR_MAX 16 + +int sdir_number = 0; +char *sdir[SDIR_MAX]; + +int rdir_number = 0; +int rdir_current = 0; +char *rdir[SDIR_MAX]; + +char got_dir[512]; + +int no_search_mod = 0; + +int +search_mod_1(char *dir, char *file) +{ + DIR *dp; + struct dirent *ptr; + int ret = 0; + + dp = opendir(dir); + if (!dp) { + fprintf(stderr, "#Cannot open %s: %s.\n", dir, + strerror(errno)); + ret = -1; + goto out; + } + while ((ptr = readdir(dp)) != NULL) { + char cdir[512]; + + if (ptr->d_type == DT_DIR) { + if (strcmp(ptr->d_name, ".") == 0) + continue; + if (strcmp(ptr->d_name, "..") == 0) + continue; + snprintf(cdir, 512, "%s/%s", dir, ptr->d_name); + if (search_mod_1(cdir, file)) { + ret = 1; + break; + } + } else { + int i; + + snprintf(cdir, 512, "%s", ptr->d_name); + for (i = 0; i < strlen(cdir); i++) { + if (cdir[i] == '_') + cdir[i] = '-'; + } + if (strcmp(cdir, file) == 0) { + snprintf(got_dir, 512, "%s/%s", dir, + ptr->d_name); + ret = 1; + break; + } + } + } + closedir(dp); + +out: + return ret; +} + +int +search_mod(char *dir, char *file) +{ + int ret; + char tmp_dir[512]; + + ret = search_mod_1(dir, file); + if (ret <= 0) + return ret; + + if (rdir_number == 0) + return 1; + + if (rdir_current >= rdir_number) + rdir_current--; + + strcpy(tmp_dir, got_dir); + snprintf(got_dir, 512, "%s%s", rdir[rdir_current], + tmp_dir + strlen(dir)); + + rdir_current++; + return 1; +} + +void +print_mod(char *name, char *addr) +{ + int i; + char mod_dir[256]; + struct stat sbuf; + char file[64]; + DIR *dp; + struct dirent *ptr; + + snprintf(file, 64, "%s.ko", name); + + if (no_search_mod) + printf("add-symbol-file %s %s", file, addr); + else { + for (i = 0; i < strlen(file); i++) { + if (file[i] == '_') + file[i] = '-'; + } + for (i = 0; i < sdir_number; i++) { + int ret; + + if (sdir[i] == NULL) + continue; + ret = search_mod(sdir[i], file); + if (ret < 0) + sdir[i] = NULL; + if (ret > 0) + break; + } + if (i >= sdir_number) { + for (i = 0; i < sdir_number; i++) { + if (sdir[i]) + break; + } + if (i >= sdir_number) { + no_search_mod = 1; + fprintf(stderr, + "#Cannot open any module search directories. Auto open -n.\n"); + } else + fprintf(stderr, + "#Cannot find file %s in the module search directories. Just output the command with filename.\n", + file); + printf("#add-symbol-file %s %s", file, addr); + } else + printf("add-symbol-file %s %s", got_dir, addr); + } + + snprintf(mod_dir, 256, "%s/%s/sections", SYS_MOD, name); + /* Check mod_dir. */ + if (stat(mod_dir, &sbuf) || !S_ISDIR(sbuf.st_mode)) { + fprintf(stderr, "%s is not a right directory.\n", mod_dir); + exit(-1); + } + + dp = opendir(mod_dir); + if (!dp) { + fprintf(stderr, "Cannot open %s: %s.\n", mod_dir, + strerror(errno)); + exit(-errno); + } + while ((ptr = readdir(dp)) != NULL) { + if (ptr->d_type == DT_REG) { + char section_file_name[512]; + FILE *fp; + char line[256]; + size_t size; + + if (strcmp(ptr->d_name, ".text") == 0 + || strcmp(ptr->d_name, ".symtab") == 0 + || strcmp(ptr->d_name, ".strtab") == 0) + continue; + + snprintf(section_file_name, 512, "%s/%s", mod_dir, + ptr->d_name); + fp = fopen(section_file_name, "r"); + if (!fp) { + perror(section_file_name); + exit(-errno); + } + if (fgets(line, 256, fp) == NULL) { + perror(section_file_name); + exit(-errno); + } + fclose(fp); + size = strlen(line); + if (size == 0) { + fprintf(stderr, "format of %s is not right.\n", + section_file_name); + exit(-errno); + } + if (line[size - 1] == '\n') + line[size - 1] = '\0'; + printf(" -s %s %s", ptr->d_name, line); + } + } + closedir(dp); + + printf("\n"); +} + +int +check_sdir(char *dir) +{ + struct stat sbuf; + + if (stat(dir, &sbuf) || !S_ISDIR(sbuf.st_mode)) { + fprintf(stderr, + "#%s is not a right directory. Ignore it.\n", + dir); + return 0; + } + + return 1; +} + +void +add_sdir(char *dir) +{ + if (sdir_number < SDIR_MAX) { + if (check_sdir(dir)) { + sdir[sdir_number] = dir; + sdir_number++; + } + } else { + fprintf(stderr, "Set too much module search directory."); + exit(-1); + } +} + +void +add_rdir(char *dir) +{ + if (rdir_number < SDIR_MAX) { + rdir[rdir_number] = dir; + rdir_number++; + } else { + fprintf(stderr, "Set too much module search directory."); + exit(-1); + } +} + +char * +get_default_sdir(void) +{ + static int need_init = 1; + static char dir[512]; + + if (need_init) { + struct utsname ubuf; + + if (uname(&ubuf)) { + fprintf(stderr, "Fail to get kernel version."); + exit(-errno); + } + snprintf(dir, 512, "%s/%s/kernel", MOD_DIR, ubuf.release); + } + + return dir; +} + +void +print_usage(char *arg) +{ + printf("Output LKM info in GDB add-symbol-file format.\n" + "Usage: %s [option]\n\n" + + " -s dir Add dir to module search directory list.\n" + " This options can use more than once.\n\n" + + " -S Add %s to module search directory list.\n\n" + + " -r dir Add dir to replace the directory.\n" + " This options can use more than once.\n\n" + + " -n No search the directory of the module\n" + " file directory.\n\n" + + " -h Display this information.\n", + arg, get_default_sdir()); + + exit(0); +} + +int +main(int argc, char *argv[], char *envp[]) +{ + struct stat sbuf; + FILE *fp; + char line[4096]; + int c; + int default_sdir_isset = 0; + + if (geteuid() != 0) { + fprintf(stderr, + "Only root can get the right address of modules.\n"); + exit(-1); + } + + while ((c = getopt(argc, argv, "s:Sr:nh")) != -1) { + switch (c) { + case 's': + add_sdir(optarg); + break; + case 'S': + if (!default_sdir_isset) + add_sdir(get_default_sdir()); + break; + case 'r': + add_rdir(optarg); + break; + case 'n': + no_search_mod = 1; + break; + case 'h': + default: + print_usage(argv[0]); + break; + } + } + + if (!no_search_mod && sdir_number == 0) + add_sdir(get_default_sdir()); + if (!no_search_mod && sdir_number == 0) { + no_search_mod = 1; + fprintf(stderr, + "#Cannot open any module search directories. Auto open -n.\n"); + } + + /* Check PROC_MOD. */ + if (stat(PROC_MOD, &sbuf) || !S_ISREG(sbuf.st_mode)) { + fprintf(stderr, "%s is not right.\n", PROC_MOD); + exit(-1); + } + + /* Get module name and address from PROC_MOD. */ + fp = fopen(PROC_MOD, "r"); + if (!fp) { + perror(PROC_MOD); + exit(-errno); + } + while (fgets(line, 4096, fp)) { + int i; + size_t size = strlen(line); + int is_not_digit = 0; + + if (line[size - 1] != '\n') { + fprintf(stderr, + "line:%s is too big to parse by getmod.\n", + line); + exit(-1); + } + + /* This part get the name. */ + for (i = 0; i < size; i++) { + if (line[i] == ' ') { + line[i] = '\0'; + break; + } + } + /* Following part will get the addr. */ + if (i == size) { + fprintf(stderr, + "The format of \"%s\" is not right.\n", line); + exit(-1); + } + if (line[size - 1] == '\n') + line[size - 1] = '\0'; + for (i = size - 2; i >= 0; i--) { + if (line[i] == ' ') { + if (is_not_digit) { + line[i] = '\0'; + is_not_digit = 0; + } else + break; + } else { + if (line[i] != 'x' && line[i] != 'X' + && (line[i] < '0' || line[i] > '9') + && (line[i] < 'a' || line[i] > 'f') + && (line[i] < 'A' || line[i] > 'F')) + is_not_digit = 1; + } + } + if (i < 0) { + fprintf(stderr, "The format of \"%s\" is not right.\n", + line); + exit(-1); + } + print_mod(line, line + i + 1); + } + if (ferror(fp)) { + perror(PROC_MOD); + exit(-errno); + } + fclose(fp); + + return 0; +}