From b5d0dbaa6bba0f01e24634886fbd06264aad016f Mon Sep 17 00:00:00 2001

From: wangshuo <wangshuo47@huawei.com>

Date: Mon, 15 Mar 2021 11:37:13 +0000

Subject: [PATCH] memory: add memtool.sh for glibc memory debug

 

glibc memory debug

The memory management of the glibc is widely used, but the methods for fault locating are limited. A command-line tool is provided to detect memory leakage of user-mode processes and collect memory distribution information about user-mode processes.

Obtains the memory distribution information of user-mode processes.

The implementation principle is to use the gcore to save the memory copy of the process, and then analyze the memory allocation of the memory copy.

memtool show -p -e -f

Detects memory leaks of user-mode processes within a period of time.

The implementation principle is to enable the memory trace mechanism of the glibc through the gdb, and then disable the trace function after a period of time.

memtool trace -p -t -f

---

doc/memtool.en.md     |  13 ++

doc/memtool.md        |  13 ++

src/memory/memtool.sh | 415 ++++++++++++++++++++++++++++++++++++++++++

3 files changed, 441 insertions(+)

create mode 100644 doc/memtool.en.md

create mode 100644 doc/memtool.md

create mode 100755 src/memory/memtool.sh

 

diff --git a/doc/memtool.en.md b/doc/memtool.en.md

new file mode 100644

index 0000000..ab6eb43

--- /dev/null

+++ b/doc/memtool.en.md

@@ -0,0 +1,13 @@

+# memtool

+

+glibc memory debug

+The memory management of the glibc is widely used, but the methods for fault locating are limited. A command-line tool is provided to detect memory leakage of user-mode processes and collect memory distribution information about user-mode processes.

+

+Obtains the memory distribution information of user-mode processes.

+The implementation principle is to use the gcore to save the memory copy of the process, and then analyze the memory allocation of the memory copy.

+memtool show -p -e -f

+

+Detects memory leaks of user-mode processes within a period of time.

+The implementation principle is to enable the memory trace mechanism of the glibc through the gdb, and then disable the trace function after a period of time.

+memtool trace -p -t -f

+

diff --git a/doc/memtool.md b/doc/memtool.md

new file mode 100644

index 0000000..992ede6

--- /dev/null

+++ b/doc/memtool.md

@@ -0,0 +1,13 @@

+# memtool

+

+glibc内存调测

+glibc的内存管理被广泛使用,但调测定位手段有限。现提供一个命令行工具,工具可以检测用户态进程内存泄漏,收集用户态进程内存分布信息。

+

+获取用户态进程内存分布信息。

+实现原理是通过gcore保存进程的内存副本, 然后分析内存副本的内存分配情况;

+memtool show -p -e -f

+

+检测用户态进程内存一段时间内的内存泄漏情况。

+实现原理是通过gdb开启glibc的内存trace机制, 然后间隔一段时间后关闭trace;

+memtool trace -p -t -f

+

diff --git a/src/memory/memtool.sh b/src/memory/memtool.sh

new file mode 100755

index 0000000..12070b3

--- /dev/null

+++ b/src/memory/memtool.sh

@@ -0,0 +1,415 @@

+#!/bin/bash

+

+#Print the usage.

+function usage()

+{

+    echo "This is a debug tool for glibc memory manage, Usage:"

+    echo "    memtool show [options]       Show the running process memory distribution"

+    echo "    memtool trace [options]      Trace the running process memory leaks"

+    echo "Selection of options:"

+    echo "    --process, -p                 Process ID"

+    echo "    --files, -f                   Path of record files"

+    echo "    --exe, -e                     Exe filename with its path"

+    echo "    --time, -t                    Time of trace (sec)"

+    echo "    --help, -h                    Print this message and then exit"

+    echo "Examples:"

+    echo "    memtool show -p PID -f path -e filename"

+    echo "    memtool trace -p PID -f path -t time"

+}

+

+function trace()

+{

+    local process_id=$1

+    local limit_time=$2

+    local file=$3

+

+    gdb attach $process_id &> /dev/null << EOF

+call setenv("MALLOC_TRACE","$file",1)

+call mtrace()

+EOF

+

+    if [ $? -eq 0 ];then

+        echo "start trace sucessed"

+    else

+        echo "start trace failed"

+        return 1

+    fi

+

+    while true

+    do

+        if [ $limit_time -gt 0 ];then

+            echo -n "$limit_time sec remaining"

+            sleep 1

+            tput rc

+            tput ed

+            let limit_time--

+            if [ ! -d /proc/${process_id} ];then

+                echo "Process does not exist."

+                return 1

+            fi

+        else

+            break

+        fi

+    done

+

+    gdb attach $process_id &> /dev/null << EOF

+call muntrace()

+call unsetenv("MALLOC_TRACE")

+EOF

+

+    if [ $? -eq 0 ];then

+        echo "stop trace sucessed"

+        return 0

+    else

+        echo "stop trace failed"

+        return 1

+    fi

+}

+

+function mm_trace()

+{

+    local process_id=-1

+    local limit_time=-1

+    local file=""

+

+    while [ $# -gt 0 ]

+    do

+        case "$1" in

+        -p|--process)

+            shift

+           if [ $# -gt 0 ];then

+                if [ -d /proc/$1 ];then

+                    process_id=$1

+                    shift

+                else

+                    echo "Invalid process id"

+                    usage

+                    exit 1

+                fi

+            else

+                echo "Missing parameters"

+                usage

+                exit 1

+           fi

+            ;;

+        -t|--time)

+            shift

+           if [ $# -gt 0 ];then

+                if [ "$1" -gt 0 ] 2>/dev/null ;then

+                    limit_time=$1

+                    shift

+                else

+                    echo "Invalid trace time"

+                    usage

+                    exit 1

+               fi

+            else

+                echo "Missing parameters"

+                usage

+                exit 1

+            fi

+            ;;

+        -f|--file)

+            shift

+            if [ $# -gt 0 ];then

+                file=$1

+                shift

+            else

+                echo "Missing parameters"

+                usage

+                exit 1

+            fi

+            ;;

+

+        *)

+            echo "Unrecognized parameters!"

+            usage

+            exit 1

+            ;;

+        esac

+    done

+

+    if [ $process_id -eq -1 ];then

+       echo "Missing Process ID"

+       usage

+       exit 1

+    fi

+

+    if [ $limit_time -eq -1 ];then

+        echo "Missing trace time"

+       usage

+       exit 1

+    fi

+

+    if [ -z $file ];then

+        echo "Missing trace file"

+        usage

+        exit 1

+    else

+        if [ ! -d ${file%/*} ];then

+            echo "invalid file path"

+            exit 1

+        fi

+    fi

+

+    trace $process_id $limit_time ${file}-tmp

+

+    if [ -f ${file}-tmp ];then

+        touch ${file}

+        chmod 600 ${file}

+        mtrace ${file}-tmp > $file

+        rm -rf ${file}-tmp

+    fi

+

+    return 0

+}

+

+#Show the running process memory distribution

+function show_mm_distribution

+{

+    local process_id=$1

+    local record_path=$2

+    local exe_file=$3

+

+    gcore -o $record_path/memtool_show_core $process_id &> /dev/null

+    gdb -q $exe_file $record_path/memtool_show_core* >> $record_path/mm_show_result.txt << EOF

+set pagination off

+set \$gdb_size_bits = 7

+set \$gdb_malloc_align_mask = 15

+set \$gdb_heap_max_size = 2 * 4 * 1024 * 1024 * sizeof(long)

+set \$gdb_loop = 1

+set \$gdb_prev_inuse = 0x0000000000000001

+define print_arena_distribution

+    if \$argc != 1

+        help print_arena_distribution

+    else

+        set \$gdb_arena_addr = (mstate)(\$arg0 + 1)

+        if \$gdb_arena_addr == &main_arena

+            set \$gdb_top = \$gdb_arena_addr->top

+            set \$gdb_top_size = \$gdb_arena_addr->top->mchunk_size &~ \$gdb_size_bits

+            set \$gdb_ptr = (unsigned long)\$gdb_top - \$gdb_arena_addr->system_mem + (unsigned long)\$gdb_top_size

+        else

+            set \$gdb_top = \$arg0->ar_ptr->top

+            if \$arg0->ar_ptr != (mstate) (\$arg0 + 1)

+                set \$gdb_ptr = (unsigned long)(\$arg0 + 1)

+            else

+                set \$gdb_ptr = (unsigned long)((mstate)(\$arg0 + 1) + 1)

+            end

+        end

+        set \$gdb_p = (mchunkptr)((\$gdb_ptr + \$gdb_malloc_align_mask) &~ \$gdb_malloc_align_mask)

+        set \$loop_stop_flag = 0

+        while \$gdb_loop == 1

+            if \$loop_stop_flag == 1

+                loop_break

+            end

+            set \$gdb_start_addr = \$gdb_p

+            set \$gdb_next_addr = (mchunkptr)((unsigned long)\$gdb_start_addr + \$gdb_start_addr->mchunk_size &~ \$gdb_size_bits)

+            set \$gdb_end_addr = \$gdb_start_addr

+            set \$gdb_original_use_flag = \$gdb_next_addr->mchunk_size & \$gdb_prev_inuse

+            set \$gdb_now_use_flag = \$gdb_next_addr->mchunk_size & \$gdb_prev_inuse

+            set \$gdb_chunk_num = 0

+            set \$gdb_all_chunk_size = 0

+            while \$gdb_now_use_flag == \$gdb_original_use_flag

+                set \$gdb_chunk_num = \$gdb_chunk_num + 1

+                set \$gdb_all_chunk_size = \$gdb_all_chunk_size + \$gdb_end_addr->mchunk_size &~ \$gdb_size_bits

+                set \$gdb_end_addr = \$gdb_next_addr

+                if \$gdb_end_addr == \$gdb_top

+                   set \$loop_stop_flag = 1

+                   loop_break

+                end

+                if \$gdb_end_addr->mchunk_size == \$gdb_prev_inuse

+                    set \$loop_stop_flag = 1

+                    loop_break

+                end

+                set \$gdb_next_addr = (mchunkptr)((unsigned long)\$gdb_next_addr + \$gdb_next_addr->mchunk_size &~ \$gdb_size_bits)

+                set \$gdb_now_use_flag = \$gdb_next_addr->mchunk_size & \$gdb_prev_inuse

+            end

+            printf "%#lx%#20lx%20ld%20ld%10d\n", \$gdb_start_addr, \$gdb_end_addr, \$gdb_chunk_num, \$gdb_all_chunk_size, \$gdb_original_use_flag

+            set \$gdb_p = \$gdb_end_addr

+        end

+    end

+end

+document print_arena_distribution

+Syntax: print_arena_distribution arena_addr

+| Print the arena memory distribution.

+end

+

+define print_mm_distribution

+    if \$argc != 0

+        help print_mm_distribution

+    else

+        set \$gdb_heap = (heap_info*)&main_arena - 1

+        set \$gdb_thread_num = 1

+        printf "\n"

+        printf "\n"

+        printf "start addr"

+        printf "            end addr"

+        printf "           chunk num"

+        printf "      all chunk size"

+        printf "     is use\n"

+        print_arena_distribution \$gdb_heap

+        set \$gdb_ar_ptr = main_arena.next

+        while \$gdb_ar_ptr != &main_arena

+            set \$gdb_thread_num = \$gdb_thread_num + 1

+            set \$gdb_heap_ptr = (heap_info *)((unsigned long)\$gdb_ar_ptr->top &~ (\$gdb_heap_max_size - 1))

+            while \$gdb_heap_ptr->ar_ptr != (mstate) (\$gdb_heap_ptr + 1)

+                print_arena_distribution \$gdb_heap_ptr

+                set \$gdb_heap_ptr = \$gdb_heap_ptr->prev

+            end

+            print_arena_distribution \$gdb_heap_ptr

+            set \$gdb_ar_ptr = \$gdb_ar_ptr->next

+        end

+        printf "Printf memory distribution Successfully !"

+    end

+end

+document print_mm_distribution

+Syntax: print_mm_distribution option

+       0 -- Print the help message

+ nothing -- Print the distribution of memory

+end

+print_mm_distribution

+EOF

+    rm -f $record_path/memtool_show_core*

+}

+

+function mm_show

+{

+    local process_id=-1

+    local record_path=""

+    local exe_file=""

+

+    while [ $# -gt 0 ]

+    do

+        case "$1" in

+        -p|--process)

+            shift

+            if [ $# -eq 0 ]; then

+                echo "Missing process id"

+                usage

+                exit 1

+            else

+                if [ "$1" -gt 0 ] 2>/dev/null; then

+                    process_id=$1

+                    shift

+                else

+                    echo "Invalid process id!"

+                    usage

+                    exit 1

+                fi

+                if [ ! -d /proc/$process_id ]; then

+                    echo "Process do not exist!"

+                    exit 1

+                fi

+            fi

+            ;;

+        -f|--files)

+            shift

+            if [ $# -eq 0 ]; then

+                echo "Missing record path"

+                exit 1

+            else

+                record_path=$1

+                if [ ! -d $record_path ]; then

+                    echo "$record_path do not exists, please create it"

+                    usage

+                    exit 1

+                fi

+                record_path=`echo $record_path | sed "s/\/$//g"`

+                shift

+            fi

+            ;;

+        -e|--exe)

+            shift

+            if [ $# -eq 0 ]; then

+                echo "Missing exe filename"

+                usage

+                exit 1

+            else

+                exe_file=$1

+                if [ ! -f $exe_file ]; then

+                    echo "Invalid exe filename"

+                    usage

+                    exit 1

+                fi

+                shift

+            fi

+            ;;

+        *)

+            echo "Unrecognized parameters!"

+            usage

+            exit 1

+            ;;

+        esac

+    done

+

+    if [ $process_id -eq -1 ];then

+       echo "Missing Process ID"

+       usage

+       exit 1

+    fi

+

+    if [ "$record_path" == "" ];then

+       echo "Record path can not be empty!"

+       usage

+       exit 1

+    fi

+

+    if [ "$exe_file" == "" ];then

+       echo "Exe filename can not be empty!"

+       usage

+       exit 1

+    fi

+

+    show_mm_distribution $process_id $record_path $exe_file

+

+    return 0

+}

+

+#judge whether gdb is installed

+which gdb &> /dev/null

+if [ $? -ne 0 ]; then

+    echo "Can not find gdb on this environment"

+    exit 1

+fi

+

+#judge whether glibc-debuginfo is installed

+ls /usr/lib/debug/sbin/ldconfig.debug &> /dev/null

+if [ $? -ne 0 ]; then

+    echo "Can not find glibc-debuginfo on this environment"

+    exit 1

+fi

+

+#judge whether glibc-debugutils is installed

+ls /usr/bin/mtrace &> /dev/null

+if [ $? -ne 0 ]; then

+    echo "Can not find glibc-utils on this environment"

+    exit 1

+fi

+

+if [ $# -lt 1 ] || [ $# -gt 7 ]; then

+    usage

+    exit 1

+fi

+

+case "$1" in

+    show)

+       shift

+        mm_show "$@"

+        exit 0

+        ;;

+    trace)

+       shift

+       mm_trace "$@"

+        exit 0

+        ;;

+    -h|--help)

+        usage

+       exit 0

+       ;;

+    *)

+        echo "Unrecognized options!"

+        usage

+        exit 1

+        ;;

+esac

+

--

2.29.2