常用排查调试工具

/**

    * @author wangdaopo

    * @email 3168270295@qq.com

    */在这里插入图片描述

工具    描述

strace    Linux分析程序运行速度瓶颈 strace是个功能强大的Linux调试分析诊断工具.尤其是针对源码不可读或源码无法再编译的程序

ulimit    linux中的资源限制?如何查看当前使用量  最好的办法是设置ulimit,以便程序在崩溃时生成核心文件。然后你可以尝试通过检查核心来找出问题所在。

objdump   反汇编定位    编译时需要给出-g,即需要调试信息  1)找基地址和程序及库  ps 查看maps 查找加载基地址cat /proc/进程号/maps  每次映射的虚拟地址空间的起始地址是会变的,不变的是其映射空间大小。2)  log: LR is at 0xb6f70b5b   3) .objdump -S /home/xxxx.so 找改库或程序出现问题位置 在偏移地址

 gdb调试

NFS

  • 常用工具介绍
  • ps: 查看进程属性
  • lsof: 查看打开的文件情况
  • netstat: 查看网络连接情况
  • strace: 查看系统调用情况

lsof,这个工具用于排查是否存在很在很多超出预料的文件的情况,比如打开某文件未关闭,建立很多的socket连接等等。当然,发现问题只能靠眼力劲了。

lsof 列举出正在使用的文件,看看是否能发现一些端倪  ,使用lsof命令查看文件库是否起效,如lsof -n | grep 文件

lsof的常见用法如下:

  • 查看特定用户打开的文件列表:lsof -u xxx
  • 查看特定端口打开的文件列表:lsof -i 8080
  • 查看特定端口范围打开的文件列表:lsof -i :1-1024
  • 基于TCP或者UDP查看打开的文件列表:lsof -i udp
  • 查看特定进程打开的文件列表:lsof -p $pid
  • 查看打开特定文件的进程列表:lsof -t $file_name
  • 查看打开特定目录的进程列表:lsof +D $file_path

memstat 它将显示每个块使用的内存量以及加载的库使用的内存量。 不是最好的工具,但它值得收集细节和统计信息。

memstat -w -p pid 是一个很好的命令。

readelf -d 工具   查看文件动态依赖库

dmesg | grep -i usb搜索包含特定字符串的被检测到的硬件

 tail -f /proc/kmsg                拔插有反应,驱动检测到

你可以用apt-cache search bluetooth看一下SARGE下的和蓝牙有关的包。

which命令的作用是,在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令

whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man说明文件(参数-m)和源代码文件(参数-s)。如果省略参数,则返回所有信息。

nl命令在linux系统中用来计算文件中行号。nl 可以将输出的文件内容自动的加上行号

$find /home/midou/logs// -mtime +30 -name "*.log.gz" -exec rm -rf {} \;

于是我想到用日期的方式删除,保存一个月的日志即可。

-exec 后面可以接任何命令,你可以灵活运用,再结合到前面的-name参数,可以玩出花来。

-exec 参数后面跟的是command,它的终止是以;为结束标志的,所以这句命令后面的分号是不可缺少的,考虑到各个系统中分号会有不同的意义,所以前面加反斜杠。

另外,下面三个的区别:

-amin n  查找系统中最后N分钟访问的文件

-atime n 查找系统中最后n*24小时访问的文件

-cmin n  查找系统中最后N分钟被改变文件状态的文件

-ctime n 查找系统中最后n*24小时被改变文件状态的文件

-mmin n  查找系统中最后N分钟被改变文件数据的文件

-mtime n 查找系统中最后n*24小时被改变文件数据的文件

 

掌握grep的常用参数,会让你查找日志或者内容非常轻松。特别是当你数据量很大的时候,没办法使用vi或者vim打开的情况下。

-d <动作>   --directories=<动作>  #当指定要查找的是目录而非文件时,必须使用这项参数,否则grep指令将回报信息并停止动作

grep常用参数示例
$grep '20:[1-5][0-9]:'    *.log  #匹配当前目录下搜索log日志中,20点的日志
$grep '20:[1-5][0-9]'     1.log 2.log 3.log  #指定在这三个文件中查找
#grep规则是支持正则表达式的
$ps -ef|grep java    #查找所有java进程
$ps -ef|grep java    #-c可以统计查找的个数
$grep '20:[1-5][0-9]:' *.log | grep -v '20:[3-4][0-9]:'   # -v反向选择,相当于过滤
$grep  'ab|bc'    *.log  

 

vim技巧 使用gtags可建立索引,让搜寻更有效率 

1. sudo apt-get install global

2.配置~/.vimrc

set cscopetag 
set cscopeprg='gtags-cscope' 
let GtagsCscope_Auto_Load = 1
let CtagsCscope_Auto_Map = 1
let GtagsCscope_Quiet = 1
let gtags_file=findfile("GTAGS", ";") 
if !empty(gtags_file)
    exe "cs add" gtags_file
endif

3.使用方法

在想要使用gtags的源码根目录执行gtags

会生成几个文件:GPATH  GRTAGS  GTAGS

这是比较强大的工具。

gtags -v

htags制作的HTML文件

htags -sanohITvt 'welcome to linux source tour'

或者:

htags -g -Fnsa

htags工具首先为你找出所有定义的Main ( )函式的档案,并且列出所在的函式。凭借htags制作的的HTML文件,你可以轻易地点击超连结,直接进到的Main ( )函式所在的程序码片段

firefox HTML/index.html

 

vim打开源码根目录下任意源码文件

Ctrl+} 跳转到函数定义处

Ctrl+t 跳转回来

systemctl 命令完全指南在运行systemd的系统上“如何控制系统和服务”

https://linux.cn/article-5926-1.html

1. 首先检查你的系统中是否安装有systemd并确定当前安装的版本

systemctl --version

2. 检查systemd和systemctl的二进制文件和库文件的安装位置

# whereis systemd

# whereis systemctl

3. 检查systemd是否运行

# ps -eaf | grep [s]ystemd

 

#查看所有进程 $ps -a

#查看进程的环境变量和程序间的关系 $ps -ef

free显示系统中可用和可用的物理内存和交换内存的总量,以及内核使用的缓冲区和高速缓存。

vmstat报告有关进程,内存,页面调度,块IO,陷阱,磁盘和cpu活动的信息。

vmstat常用参数示例

#表示在3秒时间内进行3次采样。将得到一个数据汇总他能够反映真正的系统情况。
$vmstat 3 3
#查看系统fork多少次
$ vmstat -f
    166484246 forks
#查看内存使用的详细信息
$vmstat -s
#查看磁盘的读/写
$vmstat -d
#查看系统的slab信息
$vmstat -m

通过命令行如何检测内存泄漏  smaps       以通过cat /proc/cpuinfo查看,在内核命令行中添加nosmapnosmep禁用在linux下如何向内核命令行添加命令?要看你是什zhidao么系统,比如debian或者redhat就在/boot/grub/menu.lst里面找到kernel那一行然后添加就行专了,如果不是这2个系统也可以到/boot/grub/下面去找,一般是menu.lst或者grub.conf这种的

 

目前有很多内存泄漏分析工具,比较经典的有valgrind,gperftools

Gperftools  非常实用的linux c/c++工具集 性能分析监控定位内存泄漏,寻找性能热点,提高malloc free内存分配性

                    使用简单,无需重新编译代码即可运行,对运行速度的影响也比较小。

valgrind    一个强大开源的程序检测工具,直接分析非常困难,使用valgrind会让程序运行速度变得非常慢,所以不建议使用。

利用pmap+gdb,pmap可以列出特定进程的所有内存分配的地址和大小,通过gdb就可以直接看这些地址属于什么变量,通过统计这些内存地址的大小,就可以很容易的发现问题。利用自动化的gdb调试工具也可以很方便的定位。

利用一些trace工具,比如ptrace,strace之类的工具,这些trace工具会追踪特定的api,只需要统计malloc和free的调用次数就可以简单的发现是否有泄漏,但是无法定位代码行。

dbgmem    
Electric Fence  Linux内核的Kmemleak   Kmemleak工作于内核态是内核自带的内核泄露检测工具

mtrace    GNU扩展, 用来跟踪malloc, mtrace为内存分配函数(malloc, realloc, memalign, free)安装hook函数
dmalloc    用于检查C/C++内存泄露(leak)的工具,即检查是否存在直到程序运行结束还没有释放的内存,以一个运行库的方式发布,且能够精确指出在哪个源文件的第几行。
memwatch    和dmalloc一样,它能检测未释放的内存、同一段内存被释放多次、位址存取错误及不当使用未分配之内存区域
mpatrol    一个跨平台的 C++ 内存泄漏检测器

 

readelf:

-h:文件头
-S:段表
-s:符号表
-d: 查看依赖库
-p:查看某个段内容,非常重要。如:readelf -p .comment libc.so (通过-p对只读段的查看就可以替代strings命令)

 

objdump:

-d:反汇编(objdump我基本只用这一个)
-h:段表,同readelf -S,所以可以不用记
–s:代码段、数据段、只读数据段,各个段二进制
-a:看一个.a静态库文件中包含了哪些目标文件

 

od:

如:十六进制输出数据并且地址以十进制打印:od -A d -t xCc 文件

命令中各选项的含义:
- A 指定地址基数,包括:
d 十进制
o 八进制(系统默认值)
x 十六进制
n 不打印位移值
- t 指定数据的显示格式,主要的参数有:
c ASCII字符或反斜杠序列
d 有符号十进制数
f 浮点数
o 八进制(系统默认值为02)
u 无符号十进制数
x 十六进制数
 

 

1、strace   Linux分析程序运行速度瓶颈 strace是个功能强大的Linux调试分析诊断工具,可用于跟踪程序执行时进程系统调用(system call)和所接收的信号尤其是针对源码不可读或源码无法再编译的程序

跟踪指定的系统调用列表、以指定文件名做参数、涉及进程管理、网络相关、系统信号相关、所有与进程间通信有关

例如:strace可统计分析ChangeSysTime进程所有的系统调用(strace -c ./ChangeSysTime)  注意到,settimeofday调用出错两次

假定程序源码不可修改,则此时就可借助strace找出错误所在(strace -e trace=settimeofday ./ChangeSysTime): 来调用settimeofday函数时因操作权限不够而被拒绝(需要root权限)

 

2、linux如何验证 ulimit 中的资源限制?如何查看当前使用量 

 core 大小限制 ulimit -c

查看8503进程所申请的内存大小,并非实际物理内存(RSS)大小     cat /proc/8503/status | grep VmSize

ulimit -v

如何查看某个进程的 limits,以及当前使用量?

进程自己可以通过 getrlimit()/prlimit() 来获得当前 limits 设定值。系统管理员可以通过 /proc//limits 来查看某个进程的 limits 设定值。

要查看某个进程的资源使用量通常可以通过 /proc//stat 和 /proc//status 来查看。具体某个值的含义,可以参考 proc 的手册。

另外,进程自己也可以调用 getrusage() 获知自身的资源使用量。

 

3、如何检测内存泄漏?

方法一:以下smaps是查找谁泄漏内存的几乎保证步骤:

1)找出导致内存泄漏的进程的PID。

 如果要识别哪些进程使用更多内存,

top:显示所有进程运行情况,按M键按照内存大小排序,立马看到罪魁祸首。

或请使用 ps 命令。需要将传递给 ps 以获取内存使用率最高的进程,否则进程列表将按数字排序,并为进程提供最少内存使用。

例如监视驻留内存用户的最高 15个列表。

$ watch "ps --sort -rss -eo pid,pmem,rss,vsz,comm | head -16"
 PID %MEM RSS VSZ COMMAND PID %MEM   RSS    VSZ COMMAND
 1253  9.7 351896 3041416 java
 1885  5.8 210580 2983172 gnome-shell
  709  3.8 137260 1108084 mysqld

2)捕获 /proc/PID/smaps 并保存到一些像 BeforeMemInc.txt 这样的文件中。

等待记忆增加,再次捕获/proc/PID/smaps并保存它有 afterMemInc.txt
查找第一个 smaps 和 2nd smaps 之间的差异

记下内存增加的地址范围,例如
 beforeMemInc.txt               afterMemInc.txt
---------------------------------------------------
2b3289290000-2b3289343000       2b3289290000-2b3289343000#ADDRESS


Shared_Clean: 0 kB                Shared_Clean: 0 kB 


Shared_Dirty: 0 kB                Shared_Dirty: 0 kB


Private_Clean: 0 kB                Private_Clean: 0 kB


Private_Dirty: 28 kB               Private_Dirty: 36 kB 


Referenced: 28 kB                  Referenced: 36 kB


Anonymous: 28 kB                   Anonymous: 36 kB#INCREASE MEM


AnonHugePages: 0 kB                AnonHugePages: 0 kB


Swap: 0 kB                         Swap: 0 kB


KernelPageSize: 4 kB               KernelPageSize: 4 kB


MMUPageSize: 4 kB                  MMUPageSize: 4 kB


Locked: 0 kB                        Locked: 0 kB


3)使用GDB在运行进程中转储内存,或者使用 gcore -o process 获取 coredump

在运行过程中使用gdb将内存转储到一些文件。

gdb -p PID

dump memory  ./dump_outputfile.dump 0x2b32892900000x2b3289343000   gdb 调试工具dump出可疑内存, # dump 出内存段的信息,具体要 dump 的内存段地址,可以借助之前pmap 排查的结果,以及 cat /proc/<pid>/maps 中指示的地址段得出

解释一下语法: 
- "dump memory"是命令 
- "./dump_outputfile.dump"是我们想保存dump出的内容的路径。   
- 两个hex是内存地址区间,这跟/proc/PID/smaps的格式有些不一样。这是以0x开头的而且没有连字符。

4)现在,使用 strings 命令或者 hexdump -C 来打印 dump_outputfile.dump

 strings 命令在对象文件或二进制文件中查找可打印的字符串。strings outputfile.dump  你可以得到可以读的形式,你可以在源代码中找到这些字符串。分析你的源以查找泄漏。

 

方法二:查找内存泄漏的另一个思路tcmalloc(Gperftools)

​ 注:在某些libc中程序可能要关闭检查才能正常工作。 ​

​ 注:不能检查数组删除的内存泄露,比如:char *str = new char[100]; delete str;。

x64系统的崩溃问题使用tcmalloc编译启动时宕机 解决方法:下载后重新安装 libunwind,安装到新的目录 然后重新编译 gperftools,编译之前设置 CPPFLAGS 和 LDFLAGS 为新的 libunwind 的目录,编译好后把新编出来的 gperftools 的库文件拷贝到 /usr/lib64 覆盖原来的。 重新编译被 profile 的程序,连接新编出来的 gperftools 库即可

dynamically loading libtcmalloc is unsafe (was: Dynamic linking with tcmalloc is prone to crash)

Gperftools(Google Performance Tools)是google开发的一款非常实用的工具集,主要包括:性能优异的malloc free内存分配器thread-caching malloc(tcmalloc);基于tcmalloc的堆内存检测和内存泄漏分析工具heap-profiler,heap-checker基于tcmalloc实现的程序CPU性能监测工具cpu-profiler.

如果懒得build,Ubuntu可以如下安装: 
$ sudo apt-get install libgoogle-perftool-dev google-perftools 
然后编译时加-ltcmalloc,注意一定要放最后链接,如: 
$ g++ -Wall -g problem.cpp -g -o bug -ltcmalloc 
编译时不链接的话就也可以用LD_PRELOAD: 
$ export LD_PRELOAD=”/usr/lib/libtcmalloc.so” 

运行的时候执行: 
$ HEAPCHECK=normal
./bug    使用环境变量,对整个程序进行检查

就可以报出内存泄露:​ 执行完成后会输出检查的结果,如果有泄露,pprof会输出泄露多少个字节,有多少次分配,也会输出详细的列表指出在什么地方分配和分配多少次。 ​

 

在源代码中插入检查点,这样可以控制只检查程序的某些部分,代码如下: 

​ HeapProfileLeakCheckerchecker("foo"); // 开始检查 

​ Foo(); // 需要检查的部分 ​

​ assert(checker.NoLeaks()); // 结束检查 ​

 

heap-profiler会在每当HEAP_PROFILE_ALLOCATION_INTERVAL环境变量(默认1GB常常会被我们调整为几百MB甚至更小一定量的内存被新申请分配出来时或者当一段固定时间过去后,输出含有当前内存使用情况的profile文件,文件名类似这种:
    <prefix>.0000.heap

   <prefix>.0001.heap
    <prefix>.0002.heap

​ 比较两个快照: ​​ #pprof --base=profile.0001.heap 程序名 profile.0002.heap ​

prefix是前文所说的profile数据文件的前缀,如果并没有指定成绝对路径则会在你的程序的工作目录中生成这些文件可以被一个脚本工具pprof解析输出成各种可视的数据格式文件,pprof使用了dot语言绘图,需要安装graphviz。pprof工具可以把profile信息输出成多种格式,如pdf,png,txt等

通常都是直接生成了txt文件:
%pprof --text gfs_master /tmp/profile.0100.heap

   255.6  24.7%  24.7%    255.6  24.7% GFS_MasterChunk::AddServer
   184.6  17.8%  42.5%    298.8  28.8% GFS_MasterChunkTable::Create
   176.2  17.0%  59.5%    729.9  70.5% GFS_MasterChunkTable::UpdateState
   169.8  16.4%  75.9%    169.8  16.4% PendingClone::PendingClone
    76.3   7.4%  83.3%     76.3   7.4% __default_alloc_template::_S_chunk_alloc
    49.5   4.8%  88.0%     49.5   4.8% hashtable::resize 
第一列代表这个函数调用本身直接使用了多少内存第二列表示第一列的百分比第三列是从第一行到当前行的所有第二列之和第四列表示这个函数调用自己直接使用加上所有子调用使用的内存总和第五列是第四列的百分比。基本上只要知道这些,就能很好的掌握每一时刻程序运行内存使用情况了,并且对比不同时段的不同profile数据,可以分析出内存走向,进而定位热点和泄漏。

 

在我们的实践中,也经常发现一些环境变量非常有用
HEAP_PROFILE_ALLOCATION_INTERVAL:上文说每当一定量的内存被新申请分配出来时,就会输出profile文件这个变量值就是控制多少字节默认是(1024*1024*1024)1GB,粒度相对比较大,常常会被我们调整为几百MB甚至更小
HEAP_PROFILE_MMAP:有时候程序申请内存的方式是通过mmap,sbrk系统调用而不是malloc free,如果想profile这些内存,可以开启这个变量,默认是false。们工程代码中就有些调用了mmap申请大块内存的地方,开启这个环境变量,能更准确跟踪内存走向。

export LD_PRELOAD=”/usr/lib/libtcmalloc.so” 

export PPROF_PATH=/usr/local/bin/pprof 设置pperf的环境变量:

​ TCMALLOC_DEBUG=<level> 调试级别,取值为1-2 ​

​ MALLOCSTATS=<level> 设置显示内存使用状态级别,取值为1-2 ​

​ HEAPPROFILE=<pre> 指定内存泄露检查的数据导出文件 ​

​ HEAPCHECK=<type> 堆检查类型,type=normal/strict/draconian ​

 

TCMalloc和Glibc中的ptmalloc相比更快,并可以有效减少多线程之间的竞争,因为它会为每个线程单独分配线程本地的Cache。这里先只关注它的内存相关组件。通过tcmalloc可以做heap-checking和heap-profiling。

以随时知道当前内存使用情况(程序中内存使用热点),当然也能检测内存泄漏。我们工作中一直是使用heap-profiler,实时监控程序的内存使用情况heap-profiler是基于tcmalloc的,正规开启它的方法是在代码中调用 HeapProfilerStart方法,参数是profile文件前缀名,相应的关闭则需调用 HeapProfilerStop。前面有介绍过我们的服务器有运行时执行自定义命令的机制,我们把两个命令MemProfilerStart,MemProfilerStop实现成调用上述相应的开启/关闭heap-profiler的API,这样我们可以在服务器运行时,随时开启和关闭heap-profiler,非常方便的查看当前程序内存使用情况

 

 

你也可以检查共享内存保留,但是你只知道谁是段的所有者。

Pmap分配:

sam@mail:~$ ls -l /run/shm
lrwxrwxrwx 1 root root 8 3月  21 08:05 /run/shm -> /dev/shm
sam@mail:~$ df /dev/shm/
Filesystem     1K-blocks  Used Available Use% Mounted on
tmpfs            1804956     0   1804956   0% /dev/shm
sam@mail:~$ 

注意,保留的分配要比实际分配的页面( df'要高得多

系统V 分配:

sam@mail:~$  ipcs -m 

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status   

 


 

 gdb调试步骤

抓取maps文件并转储coredump文件

1)编写coredump.sh脚本,放到/usr/local/目录

#!bin/bash

#/proc/<pid>/maps
cat /proc/$1/maps > /tmp/maps_of_$1

#coredump
dd > /tmp/core_$1_$2_$3

2)执行以下命令配置coredump

ulimit -c unlimited

echo "|/usr/local/coredump.sh %p %e %s" > /proc/sys/kernel/core_pattern

完成配置,这样发生应用程序异常之后,如果触发coredump,就会抓取/proc/<pid>/maps文件并转储coredump文件。

 

调试core文件 https://www.cnblogs.com/justin-y-lin/p/11323852.html

1)gdb调试多线程死锁,可以借助Core Dump来调试

kill -11 pid     使用 kill 命令产生 core dump文件 .用信号11杀掉它,会让进程产生一个 Segmentation Fault,从而(如果你没禁用 core dump 的话),导致一个 core dump。随后你得到一个 core 文件,里面包含了死锁的时候,进程的内存镜像

thread apply all bt  查看所用线程堆栈信息

找到_lll_lock_wait 锁等待pthread_wait /pthread_mutex_lock的地方。然后查找该锁被哪个线程锁住了

2)gdb  程序 core文件

Program terminated with signal SIGABRT, Aborted.                                /* 最开始要确定是什么信号量导致程序异常退出,这样很容易缩小范围,猜测大概可能发生了什么问题 */

2)貌似找不到符号表,库文件匹配不上,怎么办?用set solib-search-path设置一下库文件路径就好了!

3)用bt命令看一下调用栈:

(gdb) bt

#4  <signal handler called>         /* 触发信号量异常处理函数 */

#5  0xe9c71ca0 in __dynamic_cast ()

frame 4触发信号量异常处理函数,那么说明问题肯定出在frame 5。

(gdb) frame 5

#5  0xe9c71ca0 in __dynamic_cast () from /media/new/justin/2_xPON/xPON/src/config_data_gpon/squashfs-root/usr/lib/libstdc++.so.6

(gdb) info registers    打印的寄存器值

r0             0xb8d70860 3101100128
r1             0xf5d6c6c0 4124493504
r2             0xf5d6c9e0 4124494304
r3             0x0 0
r4             0xb8d70860 3101100128
r5             0xf65f1808 4133427208
r6             0x79e0 31200
r7             0xf5af8ed4 4121923284
r8             0xf5af903c 4121923644
r9             0x64 100
r10            0xf5b1a118 4122059032
r11            0xc6e581ac 3336929708
r12            0x2564 9572
sp             0xc6e58120 0xc6e58120
lr             0xf6416f70 -163483792
pc             0xe9c71ca0 0xe9c71ca0 <__dynamic_cast+16>
cpsr           0xa0070010 -1610153968

(gdb) disassemble   反汇编确定frame 5返回地址行
Dump of assembler code for function __dynamic_cast:
   0xe9c71c90 <+0>: ldr r12, [r0]
   0xe9c71c94 <+4>: push {r4, r5, r6, r7, r8, lr}
   0xe9c71c98 <+8>: mov r4, r0
   0xe9c71c9c <+12>: sub sp, sp,#40 ; 0x28
=> 0xe9c71ca0 <+16>: ldr lr, [r12, #-8]                                            /* 出错指令,将r12-8指向内存地址的数据加载到lr,通过前面打印的寄存器值发现r12此时为0x2564,在0号page,明显的非法地址访问 */

出错指令为ldr lr, [r12, #-8] 此时r12的值为0x2564,指向内存0页,为非法地址,因此该错误为非法地址访问。

上一行指令sub sp, sp, #40说明该函数的栈大小为40个字节,打印所有栈数据。

(gdb) x/40wx $sp    打印所有栈数据

0xc6e58120: 0xf5d60101 0xb8d6d50c 0xf5d6d1c4 0xf65f1808
0xc6e58130: 0xc6e5815c 0xf5d09c3c 0xc6e5815c 0x00000002
0xc6e58140: 0x0013849e 0x00080075 0xf6e05bb4 0xf65f1808
0xc6e58150: 0x000079e0 0xf5af8ed4 0xf5af903c 0xf6416f70
0xc6e58160: 0x00000000 0xf5ce3f5c 0x000079e0 0x00000415
0xc6e58170: 0x001381ac 0x00080075 0x023f3460 0x00000101
0xc6e58180: 0x023f3460 0x00080101 0x00083460 0x00000000
0xc6e58190: 0xb8d703b8 0xb8d70338 0xb8d4d4b0 0x8000007a
0xc6e581a0: 0xf6e05bb4 0x00000008 0xc6e60944 0xf6cfa60c
0xc6e581b0: 0xc6e60870 0x00000001 0x00000001 0x00000001

 (gdb) list *0xf6416f70
0xf6416f70 is in cfg_mod_set_onu_local_mng_interface_config_flag(PON_NO, ONU_NO, unsigned char) (/media/new/linyao/2_xPON/xPON/src/config_data_gpon/config_module_cinterface.cpp:35769).
35764         MAPLE_LOG(ULLOG_PRI_ERROR, "onuno %d\r\n", onu_no);
35765         return ERR_INVALID_ONU_NO;
35766     }
35767 
35768     /* »?嗃¶Տ?
35769     COnuConfigObject * onu_config_object = ONU_CONFIGOBJECT(pon_no, onu_no);
35770     if (NULL != onu_config_object)
35771     {
35772         /* ¼ԋ
35773         synchronized(onu_config_object)

此时pc地址在libstdc++.so.6库的代码段__dynamic_cast函数 lr地址(函数返回地址)在libconfig_xxx.so的代码段,即调用ONU_CONFIGOBJECT()宏处。

再回溯一条指令,是push {r4, r5, r6, r7, r8, lr}压栈指令,lr压栈,在栈数据中找到lr值,在0xc6e5815c处,由此反推出r4~r8的值:

再往上一条指令时ldr r12, [r0],将r0指向内存的值加载到r12寄存器中。
这几条指令中,r0的值没有发生改变,打印r0指向内存区域的值。

(gdb) x/64wx $r0-64 (打印以r0指向地址为中心的128字节数据)
0xb8d70820: 0x00000000 0x00000000 0x00000000 0x00000000
0xb8d70830: 0xfe0f0000 0xffffffff 0x0a00ffff 0x0e888888
0xb8d70840: 0xf90f0081 0x01000608 0x04060008 0x0a000100
0xb8d70850: 0x0e888888 0x0e64190a 0x00000000 0x190a0000
0xb8d70860: 0x00002564 0x00000000 0x00000000 0x00000000

r0的值为0x2564,即前面出问题的地址。那么,r0的值从哪里来的呢?带着疑问进入上一层函数

(gdb) frame 6

(gdb) info frame

反汇编确定frame 6返回地址行

(gdb) disassemble (根据寄存器值和栈数据,返回代码执行流程,灰色为不执行代码,蓝色为跳转地址)

(gdb) p/x $r11-32

$1 = 0xc6e5818c

分析r1 = [r11 - 32],此时r1寄存器值为0xf5d6c6c0,但是栈数据中保存的值为0x0000 0000,说明栈数据被踩了。

(gdb) x/wx 0xc6e5818c

0xc6e5818c: 0x00000000

分析r0 = r3 = [r11 - 30], r11是栈基址,sp是栈顶指针。此时r0寄存器值为0xb8d70860,但是栈数据中保存的值为0x03b8 0000,证明栈数据果然被踩了。

(gdb) p/x $r11-30
$2 = 0xc6e5818e
(gdb) x/wx 0xc6e5818e
0xc6e5818e: 0x03b80000

那么栈数据被谁踩了呢?茫茫进程中,万千线程,怎么破?

(gdb) info registers 

查看一下栈数据,貌似没有发现什么异常。

(gdb) x /128wx $sp-256

既然出问题的r0指向的内存地址,而且0x2564地址出现的很诡异,明显也是被踩了,那就看一下r0指向的地址附近的数据

查看以r0指向地址为中心的128字节数据

(gdb) x /64wx $r0-64

0xb8d70860: 0x00002564 0x00000000 0x00000000 0x00000000                           ------->0x2564赫然在列!

以字节形式显示:

(gdb) x/128bx $r0-64

0xb8d70830: 0x00 0x00 0x0f 0xfe 0xff 0xff 0xff 0xff                                   ---->ff:ff:ff:ff:ff:ff 目的MAC地址,广播地址
0xb8d70838: 0xff 0xff 0x00 0x0a 0x88 0x88 0x88 0x0e                             ----->源MAC地址
0xb8d70840: 0x81 0x00 0x0f 0xf9 0x08 0x06 0x00 0x01                           -----> 0x8100 帧类型 ARP数据包

这个时候,大神出现,一眼就看出了踩内存的这一段数据是一个ARP网络数据包。赶紧ifconfig看一下:

源MAC地址和eth0的MAC地址完全一致,终于抓到了罪魁祸首!!!

最终定位是网卡驱动使用了一段保留内存段,但是Linux内核初始化DDR时,保留内存段设置没有对齐,导致该内存段参与了动态内存分配,用户态进程和内核网卡驱动同时往这段内存写数据,ARP报文踩了进程的子线程的栈,导致该异常发生。

 

gdb交叉调试操作步骤
    把生成的gdbserver拷贝到NFS共享目录下,因为gdbserver最终是在目标机上运行:
   1) 测试gdbserver
        a)编译一个简单的程序helloworld.c
            arm-linux-gcc –g helloworld.c –o helloworld 
            cp helloworld /nfsboot
        b)库问题
            需要注意的是运行gdbserver还需要libthread_db库,如果你做的文件系统里没有相应的库,需要从交叉编译器内拷贝一个过去。
            cp –avf /usr/local/arm/4.2.2-eabi/lib/libthread_db*   /nfsboot/lib
        c)到超级终端上启动gdbserver
            ./gdbserver 192.168.1.10:2345 helloworld
                注意:我用的HOST IP是192.168.1.10,2345是监听端口,可以随便设定
        d)然后在主机上启动gdb:
            cd /nfsboot    
            arm-linux-gdb helloworld
            链接到开发板上:
                target remote 192.168.1.128:2345
                    注意:192.168.1.128是开发板的IP,2345是监听端口,要与上面设置的相同
        e)设置一下gdb环境变量
             show solib-absolute-prefix 

             set solib-absolute-prefix /usr/local/arm/4.2.2-eabi/lib/
                usr/local/arm/4.2.2-eabi/lib/ 是交叉编译器的库路径/opt/hisi-linux-nptl/arm-hisiv100-linux/target/lib/
                    
             show solib-search-path 

             set solib-search-path /nfsboot/lib/
                /nfsboot/lib是文件系统的库路径
  
            到此环境已经搭建好,现在可以像在Linux下用GDB调试程序了。
            
   2) GDB的使用
        OK,现在就可以开始交叉调试了。
            a) 把之前的helloworld修改一下:
            b)交叉编译生成可执行文件之后,放到NFS的挂载目录下
               i.目标板
                在你在开发板上运行gdbserver前,先设置一下库文件搜索路径 LD_LIBRARAY_PATH
                 执行:
                    ./gdbserver 192.168.1.10:2345 helloworld   注意:我用的HOST IP是192.168.1.10  2345 是监听端口,可以随便设定
                    
                    
                ii.在HOST上执行:
                    arm-hisiv100nptl-linux-gdb  helloworld
                    (gdb) show solib-absolute-prefix  
                    (gdb) set solib-absolute-prefix  /opt/hisi-linux-nptl/arm-hisiv100-linux/target/lib/ 编译器安装目录下的lib
                        
                        
                    (gdb) show solib-search-path 

                        set solib-search-path   设置应用依赖库路径  文件系统的库路径 
                            
                       info sharedlibrary
                    target remote 192.168.5.197:2345   192.168.5.197 是开发板的IP, 2345 是监听端口,要与上面设置的相同
                        info sharedlibrary
                    输入c继续后会在目标板终端出现输出信息
                        然后在目标板终端输入相应的值进行操作
                            操作后的输出信息会在host主机终端显示,然后bt查看堆栅信息进行问题跟踪
                 
                 开始对程序进行调试:
            c)GDB的常用指令  gdb help all 帮助信息
                       load:装入一个程序;    
                       symbol-file:装入符号库文件,可以是用-g参数编译的可执行文件;    
                       f(ile):指定一个可执行文件进行调试,gdb将读取些文件的调试讯息,如f a.exe;   
                       l(ist):列程序出源文件;    

                       info break 打印程序断点信息

                        set args Shaw 设置主函数输入参数args
                       r(un) :装载完要调试的可执行文件后,可以用run命令运行可执行文件;    
                       b(reak):设置断点(break point),如b 25,则在源程序的第25行设置一个断点,当程序执行到第25行时,就会产生中断;也可以使用通过函数名设置断点 b funcname,funcname为函数的名称;通过文件名行号设置断点  break test.c:37 
                当程序运行到断点停下来时:
                      c(ontinue):c命令可以另中断的程序继续执行,直到下一个中断点或程序结束;   
                      p(rint):输入某个变量的值,如程序定义了一个int a的就是,p a就会输出aa的当前值;

                    print i  打印i变量值

                     set var i=100 设置i变量值    
                      n(ext):程序执行到断点时中断执行,可以用n指令进行单步执行;    
                      s(tep):程序执行到断点时中断执行,可以用s指令进行单步执行进某一函数;   
                      q(uit):退出GDB

                   这里写图片描述

                     这里写图片描述

                linux下gdb调试方法与技巧整理
               https://blog.csdn.net/niyaozuozuihao/article/details/91802994?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

            gdb调试时,编译程序注意
                需要在编译程序时关闭编译优化。
                编译(带-g 不带-o2,
                不要strip
                    文件一旦strip后就不能恢复原样了,所以strip是一个减肥工具而不是压缩工具。而且,被strip后的文件不包含调试信息,就不能用dbx来调试程序了。
                        2.cc 编译时加上"-s"参数,具有同样的作用。
                (gdb) directory ../zion/
                    Source directories searched: /mnt/nfs/ca_dvr_project/../zion:$cdir:$cwd

 

Valgrind---内存调试,内存泄漏检测以及性能分析的软件开发工具

用valgrind监测内存泄漏,不用重新编译应用程序,不用重新链接应用程序,不用对应用进程做任何修改。如果想查看详细的出错信息,只需要在编译时加上-g选项。一些编译优化选项(比如-O2或者更高的优化选项),可能会使得memcheck提交错误的未初始化报告,因此,为了使得valgrind的报告更精确,在编译的时候最好不要使用优化选项。

valgrind默认使用memcheck工具做内存监测。

在用C/C++编程的时候,经常会出现下面三种内存问题:

  • 内存泄漏
  • 悬挂指针
  • 多次释放同一块内存

用valgrind对代码进行内存检测的时候,如果提示“Conditional jump or move depends on uninitialised value(s)”,有可能是某些变量未初始化造成的。

 valgrind --leak-check=yes --show-reachable=yes ./core2

不管valgrind在使用memcheck工具监测内存时,它会管应用程序,并且读取应用程序可执行文件和库文件中的debug信息来显示详细的出错位置。当valgrind启动后,应用 进程实际上在valgrind的虚拟环境中执行,valgrind会将每行代码传递给memcheck工具,memcheck工具再加入自己的调试信息,之后再将合成的代码真正运行。memcheck工具在 应用进程每个防存操作和每个变量赋值操作时加入额外的统计代码,通常情况下,使用memcheck工具应用程序的运行时间会比原生代码慢大约10-50倍

其次对于一些不停机运行的服务器程序的内存问题,valgrind无能为力不仅仅是因为valgrind无法使之停止,还有可能是因为服务器进程本身就被设计为申请一些生命周期 与进程生命周期一样长的内存,永远不释放,这些内存会被valgrind报泄漏错误。

再次,valgrind对多线程程序支持得不够好。在多线程程序执行时,valgrind在同一时刻只让其中一个线程执行,它不会充分利用多核的环境。在用valgrind运行您的多线程程序 时,您的宝贵程序的运行情况可能跟不使用valgrind的运行情况千差万别。
   

 通常使用到的选项有:--tool=memcheck (指定工具)
 
                                --leak-check=full(设置检测所有内存问题)
 
                                --show-leak-kinds=all(设置提示所有内存泄漏问题)
 
                                --track-origins=yes
 
                                --log-file=valgrind.log(设置检测的日志信息文本)
 
                                --track-fds=yes(设置检测文件描述符)
 
                                --time-stamp=yes(设置在日志文件中加入时间戳)

Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。

Callgrind。它主要用来检查程序中函数调用过程中出现的问题。检查代码覆盖和性能瓶颈

Cachegrind。它主要用来检查程序中缓存使用出现的问题。

Helgrind。它主要用来检查多线程程序中出现的竞争问题。能够发现程序中潜在的条件竞争

Massif。它主要用来检查程序中堆栈使用中出现的问题。

Extension。可以利用core提供的功能,自己编写特定的内存调试工具

 

检查代码覆盖和性能瓶颈:我们调用valgrind的工具执行程序:

#valgrind --tool=callgrind./sec_infod

会在当前路径下生成callgrind.out.pid(当前生产的是callgrind.out.19689),如果我们想结束程序,可以:

#killall callgrind

然后我们看一下结果:

#callgrind_annotate --auto=yes callgrind.out.19689   >log

callgrind ------> 它主要用来检查程序中函数调用过程中出现的问题  它生成的结果非常详细,甚至连函数入口,及库函数调用都标识出来了。

 Callgrind的生成调用图过程吧,执行:valgrind --tool=callgrind ./tmp,执行完成后在目录下生成"callgrind.out.XXX"的文件这是分析文件,可以直接利用:callgrind_annotate callgrind.out.XXX 打印结果,也可以使用:gprof2dot.py -f callgrind callgrind.out.XXX |dot -Tpng -o report.png 来生成图形化结果:

https://pypi.org/project/gprof2dot/#files

 

利用linux的mtrace命令定位内存泄露(Memory Leak)   无非就是记录每一对malloc/free的调用情况

#include <stdio.h>

int main()
{
        setenv("MALLOC_TRACE", "taoge.log", "1");
        mtrace();

        int *p = (int *)malloc(2 * sizeof(int));

        return 0;
}

  编译:gcc -g -DDEBUG test.c   (千万要注意, -g不可漏掉。

  执行:./a.out

    定位:mtrace a.out taoge.log

能够看到, 有内存泄露,且正确定位到了代码的行数。

 

 

dmalloc是一个简单易用的C/C++内存leak检查工具,以一个运行库的方式发布。

dmalloc能够检查出直到程序运行结束还没有释放的内存,并且能够精确指出在
哪个源文件的第几行。

dmalloc 主页: http://dmalloc.com

支持的平台:AIX, BSD/OS, DG/UX, Free/Net/OpenBSD, GNU/Hurd, HPUX, Irix, Linux, MS-DOG, NeXT, OSF, SCO, Solaris, SunOS, Ultrix, Unixware, Windoze, and even Unicos on a Cray T3E

最新版本: 5.5.2

安装:下载 http://dmalloc.com/releases/dmalloc-5.5.2.tgz

 

  1. tar zxvf dmalloc-5.5.2.tgz

  2. cd dmalloc-5.5.2

  3. ./configure

  4. make

  5. make install

  6. 设置环境变量:
    对于 Bash, ksh, and zsh (http://www.zsh.org/), 在 `.bashrc', `.profile', or `.zshrc'
    文件中加入一行 ( -b 参数表示针对Bash的输出):

    function dmalloc { eval `command dmalloc -b $*`; }

    然后重新登陆用户,或者执行: source ~/.bashrc 或 source ~/.profile

    接下面执行:

  7. dmalloc -l logfile -i 100 low

  8. 在源文件中添加下面的C代码

    #ifdef DMALLOC
    #include "dmalloc.h"
    #endif

    编译使用的CFLAGS: -DDMALLOC -DDMALLOC_FUNC_CHECK

    如: gcc -DDMALLOC -DDMALLOC_FUNC_CHECK dm_test.c

    执行:
    ./a.out

运行上面的命令会在当前目录下生成 logfile文件,查看logfile的内容如下:

cat logfile

  1. 1214894489: 2: Dmalloc version '5.5.2' from 'http://dmalloc.com/'

  2. 1214894489: 2: flags = 0x4e48503, logfile 'logfile'

  3. 那么,哪个地方的内存leak就一目了然了。

  4. //====== dm_test.c 源代码 =============

  5. #include <stdio.h>

  6. #include <stdlib.h>

  7. #include <string.h>

  8. #ifdef DMALLOC

  9. #include <dmalloc.h>

  10. #endif

  11. int main(int argc, char **argv)

  12. {

  13.     char *str;

  14.     str = malloc(5);

  15.     str = malloc(6);

  16.     return 0;

  17. }

  18.  

Linux内核的Kmemleak

.1 Kmemleak检测工具介绍

Kmemleak工作于内核态是内核自带的内核泄露检测工具, 其源代码位于mm/kmemleak.c

Kmemleak工作于内核态,Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。当独立的对象没有被释放时,其报告记录在 /sys/kernel/debug/kmemleak中,Kmemcheck能够帮助定位大多数内存错误的上下文。

.2 Kmemleak使用过程概述

首先`CONFIG_DEBUG_KMEMLEAK在Kernel hacking中被使能.

查看内核打印信息详细过程如下:

挂载debugfs文件系统 
mount -t debugfs nodev /sys/kernel/debug/
开启内核自动检测线程 
echo scan > /sys/kernel/debug/kmemleak
查看打印信息 
cat /sys/kernel/debug/kmemleak
清除内核检测报告,新的内存泄露报告将重新写入/sys/kernel/debug/kmemleak 
echo clear > /sys/kernel/debug/kmemleak
内存扫描参数可以进行修改通过向/sys/kernel/debug/kmemleak 文件写入。 参数使用如下

off 禁用kmemleak(不可逆)
stack=on 启用任务堆栈扫描(default)
stack=off 禁用任务堆栈扫描
scan=on 启动自动记忆扫描线程(default)
scan=off 停止自动记忆扫描线程
scan=<secs> 设置n秒内自动记忆扫描
scan 开启内核扫描
clear 清除内存泄露报告
dump=<addr> 转存信息对象在<addr>
通过“kmemleak = OFF”,也可以在启动时禁用Kmemleak在内核命令行。在初始化kmemleak之前,内存的分配或释放这些动作被存储在一个前期日志缓冲区。这个缓冲区的大小通过配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE设置。
 

 

 

 

 

 

 

 

Linux调试分析诊断利器——strace

   strace是个功能强大的Linux调试分析诊断工具,可用于跟踪程序执行时进程系统调用(system call)和所接收的信号,尤其是针对源码不可读或源码无法再编译的程序。

     在Linux系统中,用户程序运行在一个沙箱(sandbox)里,用户进程不能直接访问计算机硬件设备。当进程需要访问硬件设备(如读取磁盘文件或接收网络数据等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace可跟踪进程产生的系统调用,包括参数、返回值和执行所消耗的时间。若strace没有任何输出,并不代表此时进程发生阻塞;也可能程序进程正在自己的沙箱里执行某些不需要与系统其它部分发生通信的事情。strace从内核接收信息,且无需以任何特殊方式来构建内核。

     例如,命令strace -o out.txt -T -tt -e trace=all -p 2899表示跟踪2899进程的所有系统调用,并统计系统调用的时间开销,以及调用起始时间(以可视化的时分秒格式显示),最后将记录结果存入out.txt文件。 

 

     通过使用-c选项,strace可统计分析进程所有的系统调用(strace -c ./ChangeSysTime),如:

     可看到程序调用哪些系统函数,调用次数、所耗时间及出错次数等信息,有助于分析程序运行速度瓶颈。同时注意到,settimeofday调用出错两次,而该函数在ChangeSysTime程序中被显式调用两次,亦即这两次调用均出错!但ChangeSysTime程序中并未对settimeofday调用作出错处理,故在运行中没有输出任何错误提示。假定程序源码不可修改,则此时就可借助strace找出错误所在(strace -e trace=settimeofday ./ChangeSysTime): 

     真相大白,原来调用settimeofday函数时因操作权限不够而被拒绝(需要root权限)!注意,第2、3和5行输出为ChangeSysTime程序打印输出。

-e trace=set:跟踪指定的系统调用列表,如-e trace=open,close,read,write表示只跟踪这四种系统调用。默认为set=all。

-e trace=file:跟踪以指定文件名做参数的所有系统调用。

-e trace=process:跟踪涉及进程管理的所有系统调用,可用于观察进程的fork、wait和exec阶段。

-e trace=network:跟踪网络相关的所有系统调用。

-e strace=signal:跟踪所有与系统信号相关的系统调用。

-e trace=ipc:跟踪所有与进程间通信有关的系统调用。

https://www.cnblogs.com/clover-toeic/p/3738156.html

 

 

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 46898
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65536
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 46898
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

补充说明:ulimit为shell内建指令,可用来控制shell执行程序的资源。

参 数:
   -a 显示目前资源限制的设定。 
   -c <core文件上限> 设定core文件的最大值,单位为区块。 
   -d <数据节区大小> 程序数据节区的最大值,单位为KB。 
   -f <文件大小> shell所能建立的最大文件,单位为区块。 
   -H 设定资源的硬性限制,也就是管理员所设下的限制。 
   -m <内存大小> 指定可使用内存的上限,单位为KB。 
   -n <文件数目> 指定同一时间最多可开启的文件数。 
   -p <缓冲区大小> 指定管道缓冲区的大小,单位512字节。 
   -s <堆叠大小> 指定堆叠的上限,单位为KB。 
   -S 设定资源的弹性限制。 
   -t <CPU时间> 指定CPU使用时间的上限,单位为秒。 
   -u <程序数目> 用户最多可开启的程序数目。 
   -v <虚拟内存大小> 指定可使用的虚拟内存上限,单位为KB。

如何查看某个进程的 limits,以及当前使用量?

进程自己可以通过 getrlimit()/prlimit() 来获得当前 limits 设定值。系统管理员可以通过 /proc//limits 来查看某个进程的 limits 设定值。

要查看某个进程的资源使用量,通常可以通过 /proc//stat 和 /proc//status 来查看。具体某个值的含义,可以参考 proc 的手册。

另外,进程自己也可以调用 getrusage() 获知自身的资源使用量。

在没有as限制的时候,进程能成功申请到 1000MB 空间:

[root@rhel674 ~]# ulimit -v
unlimited
[root@rhel674 ~]# cat /proc/8503/status | grep VmSize
VmSize:  1027928 kB

如果限定 as 为 500MB,则无法成功申请:

[test@rhel674 ~]$ ulimit -v
500000
[test@rhel674 ~]$ /tmp/big_as 
malloc failed.

 

objdump反汇编用法示例

  • -d:将代码段反汇编
  • -S:将代码段反汇编的同时,将反汇编代码和源代码交替显示,编译时需要给出-g,即需要调试信息。
  • -C:将C++符号名逆向解析。
  • -l:反汇编代码中插入源代码的文件名和行号。
  • -j section:仅反汇编指定的section。可以有多个-j参数来选择多个section

$objdump -d a.out # 简单反汇编

$objdump -S a.out # 反汇编代码中混入对应的源代码

$objdump -C -S a.out # C++符号名逆向解析

objdump -j .text -l -C -S a.out # 打印源文件名和行号

 

NFS

PC端
sudo apt-get install nfs-kernel-server
sudo /etc/init.d/nfs-kernel-server start
vim /etc/exports
该文件写入
/home/nfs 192.168.*.*(rw,sync,no_root_squash)

嵌入式设备端
mount -n -o nolock 192.168.1.3:/home/nfs/ /mnt/ && cd /mnt

 

top程序提供正在运行的系统的动态实时视图它可以显示系统摘要信息以及Linux内核当前正在管理的进程或线程的列表。所显示的系统摘要信息的类型以及为进程显示的信息的类型,顺序和大小都是用户可配置的,并且可以使配置在重新启动后保持不变。
        该程序为流程操作提供了一个有限的交互式界面,并为个人配置提供了更为广泛的界面-涵盖了其操作的各个方面。尽管在本文档中始终引用top,但是您可以随意为程序命名。然后,该新名称(可能是别名)将反映在顶部的显示屏上,并在读写配置文件时使用。

sam@mail:~$ top
top - 17:08:03 up 47 min,  1 user,  load average: 0.00, 0.01, 0.05
Tasks: 142 total,   1 running, 141 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.7 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3609916 total,   942256 free,  1024820 used,  1642840 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  2276428 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                        
 1393 jenkins   20   0 3040388 343216  33576 S  0.3  9.5   0:54.30 java                                                                                           
 2083 gdm       20   0 2994492 223000  94308 S  0.0  6.2   0:18.34 gnome-shell                                                                                    
 1112 git       20   0 2889520 206224  21528 S  0.3  5.7   0:23.25 java                                                                                           
  685 mysql     20   0 1108672 147908  17272 S  0.3  4.1   0:01.91 mysqld                                                                                         
 2164 gdm       20   0  478204  47276  37788 S  0.0  1.3   0:00.11 ibus-x11                                                                                       
  893 root      20   0  335496  37156  30736 S  0.0  1.0   0:00.22 apache2           

解释

第一行,任务队列信息,同 uptime 命令的执行结果,具体参数说明情况如下:

00:56:07 — 当前系统时间

up 149 days, 14:40 — 系统已经运行了149天14小时40分钟(在这期间系统没有重启过的)

1users — 当前有1个用户登录系统

load average: 0.00, 0.02, 0.05 — load average后面的三个数分别是1分钟、5分钟、15分钟的负载情况。

load average数据是每隔5秒钟检查一次活跃的进程数,然后按特定算法计算出的数值。如果这个数除以逻辑CPU的数量,结果高于5的时候就表明系统在超负荷运转了。

第二行,Tasks — 任务(进程)

系统现在共有254个进程,其中处于运行中的有1个,253个在休眠(sleep),stoped状态的有0个,zombie状态(僵尸)的有0个。

第三行,cpu状态信息

%Cpu(s):  1.4 us,  0.3 sy,  0.0 ni, 98.3 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st

1.4 us — 用户空间占用CPU的百分比。

0.3 sy — 内核空间占用CPU的百分比。

0.0 ni — 改变过优先级的进程占用CPU的百分比

98.3 id — 空闲CPU百分比

0.1 wa — IO等待占用CPU的百分比

0.0 hi — 硬中断(Hardware IRQ)占用CPU的百分比

0.0 si — 软中断(Software Interrupts)占用CPU的百分比

第四行,内存状态

65808884 total  物理内存总量

23749772 free  使用中的内存总量

4586160 used   空闲内存总量

37472952 buff/cache  缓存的内存量

第五行,swap交换分区信息

0 total   交换区总量

0 use   使用的交换区总量

0 free   空闲交换区总量

60909608 avail Mem  可用交换区总量

第七行以下:各进程(任务)的状态监控

PID — 进程id

USER — 进程所有者

PR — 进程优先级

NI — nice值。负值表示高优先级,正值表示低优先级

VIRT — 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES

RES — 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA

SHR — 共享内存大小,单位kb

S — 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程

%CPU — 上次更新到现在的CPU时间占用百分比

%MEM — 进程使用的物理内存百分比

TIME+ — 进程使用的CPU时间总计,单位1/100秒

COMMAND — 进程名称(命令名/命令行)

 

 

文件头:描述文件的基础信息以及找到段表

文件头查看:readef -h

1)模数
第1个字节:DEL控制符,固定的
第2、3、4个字节:ELF三个字母
第5个字节:文件位数(0:无效、1:ELF32、2:ELF64)
第6个字节:大小端(0:无效、1:小端、2:大端)
第7个字节:ELF文件的主版本号,一般为1
后面的为预留字段。
2)文件类型:REL (Relocatable file)、DYN (Shared object file)、EXEC (Executable file)
3)Machine:   Intel 80386 :硬件平台,此文件适合在哪个硬件平台下运行        
4)程序入口地址
  Entry point address:               0x8048310
代表程序入口的虚拟地址,只有可执行文件跟共享库有入口(共享库的入口地址有什么用呢?)

5)Start of program headers:   52 (bytes into file) : (readelf -l ), 程序表头,装载时用到,因为目标文件不需要装载,所以为0。
6)Start of section headers:          2000 (bytes into file)
      段表在文件中的偏移,也就是说段表从文件中的2000个字节开始的
      2000是10进制,用计算器转换成16进制后就可以在那个,这个特别重要,找到段表,就代表了什么都有了
7)Size of section headers:           40 (bytes)
段表描述符的大小,猜测:会根据平台的不同,这个是固定的,因为在同一个平台下不同代码编出来的目标文件,结果是一样的
8)Number of section headers:         11
段表描述符数量,elf文件中,拥有的段的数量(所以说,一个段表描述符就是在描述一个段呗)
证明(readelf -S )
9)Size of this header:     52 (bytes)
ELF文件头本身的大小,猜测:会根据平台的不同,这个是固定的
10)Section header string table index: 8
段表字符串表所在的段在段表中的下标

总结:通过文件头找段表,之后在段表里面找到段表字符串表,段表中就能找到段表字符串标在文件中的偏移,之后就能生成整个段表。

 

段表:描述每个段的详细信息

段表查看:readelf –S, 如果gcc –g的话,还会有很多调试信息段 

 

Name:在段表描述符里存了一个整形,这个整形是段名在段表字符串表(.shstrtab)中的偏移。
Type:段的类型,如下表
Addr:如果可以被加载,代表了该段在虚拟内存空间的地址,否则为0,可以看出来,段表字符串表、符号表、字符串表都不会被加载的
Off:如果该段在文件中,则代表了在文件中的偏移
Size:段长
Flags:标志
Lk跟Inf:段的链接信息
Al:段地址对齐,代表这个段是几字节对齐的
ES:项的长度:如果该段的每一项的长度是固定的,那么就代表每一项的长度,如符号表的每个符号的信息长度都是0x10个字节。对于变化的就是0。符号表里存的符号名,只是符号在字符串表中的索引,符号表里不是直接存的符号。


Flags字段:段的标志,表示该段在进程虚拟空间中的属性,可读/可写/可执行,这个标志比较重要,因为在加载的时候,会根据段的标志进行加载的,大概是只读放在一个区域,可读可写放在一个区域等

Lk跟Inf:段的链接信息,与链接、重定位相关的段,这两个信息才有意义

重要段:代码段、数据段、只读数据段、BSS段

代码段、数据段、只读数据段:objdump –s
代码段:程序指令,objdump -d反汇编
数据段:已经初始化了全局变量和局部静态变量
只读数据段:只读变量以及字符串常量
BSS段:存放了未初始化的全局变量和局部静态变量,通过符号表可以看出

字符串表:符号名等


段表字符串表:段名

注释提示段

符号表:readelf -s / nm / objdump -x

符号名:该符号在字符串表中的下标
符号值:目标文件中:非COMMON类型表示该符号在所在段中的偏移,common类型表示对齐属性;可执行文件中:代表虚拟地址(动态链接器如何用它?)
符号大小:非0代表该符号值的数据类型的大小,0代表未知或0
符号类型和绑定信息:符号类型:数据?函数?段?文件?符号绑定信息:局部符号(外部不可见)?全局符号(外部可见)?弱引用?低4位表示符号的类型,高28位表示符号的绑定信息,看下表
其他:目前没用,为0
符号所在的段:如果非0数字,代表了是在本文件中定义的符号,并且代表了对应的段,其余含义为:UND:本目标文件未定义;ABS:绝对的值,如文件名;COMMON:COMMON块类型(未初始化的全局变量)

 

 

 

scp是非常方便的远程复制工具:
下载:scp [-r] 用户名@服务器:路径 本地路径
上传:scp [-r] 本地路径用户名@服务器:路径
上传下载时要注意,权限问题。
上传:本地要有读取和执行权限,对远程主机要有读取、执行、写入。
下载:本地要有读取、执行、写入,对远程主机要有读取和执行权限。

步骤一:在主机A上使用scp下载文档 scp [-r] 用户名@服务器:路径 本地路径
将主机B上的root@192.168.56.1:/文件复制到主机A上/opt下,命令操作如下所示:

sam@mail:~$ scp root@192.168.56.1:/文件  /opt/

 

步骤二:在主机A上使用scp上传文档   上传:scp [-r] 本地路径用户名@服务器:路径

将本地主机A上的~/test1.pub 文件复制到主机B上用户root的根目录下,以用户root的密码验证。
在A上操作,命令操作如下所示:

sam@mail:~$ scp ./test1.pub root@192.168.56.1:/

在B上操作,查看验证。命令操作如下所示:

root@OpenWrt:/# ls

 test1.pub 

 

在VirtualBox中的Ubuntu中添加新硬盘


步骤如下:

1. 关闭Ubuntu系统,打开VistualBox,"设置"->"存储"->“添加虚拟硬盘”

2. 启动Ubuntu系统,操作命令如下:
#1 sudo fdisk -l // 查看现有系统磁盘空间
以上信息可以看到新增加的磁盘空间 /dev/sdb ,这里我们需要给新的磁盘空间分区。
#2 fdisk /dev/sdb
#3 Command (m for help): m // 键入m,可看到帮助信息
#4 Command (m for help): n
打印结果如下:
键入:p,选择添加主分区;
键入:1,选择主分区编号为1, 这样创建后的主分区为sdb1;
这样我们就创建完一个分区,如果要创建更多分区可以照上面的步骤继续创建。
最后,键入:w,保存所有并退出,完成新磁盘的分区。
重启


4. 格式化磁盘分区
#7 sudo mkfs -t ext4 /dev/sdb1 // 用ext3格式对 /dev/sdb1 进行格式化

5. 挂载分区
#8 sudo mkdir /data // 创建新的挂载点
#9 sudo mount /dev/sdb1 /data // 将新磁盘分区挂载到 /data 目录下
#10 sudo df // 查看挂载结果


6. 开机自动挂载
#11 vi /etc/fstab // 修改 /etc/fstab 文件
在 /etc/fatab 文件中,添加如下内容:
/dev/sdb1 /data ext4 defaults 1 2

 

将挂载盘的目录软连接到/home/wdp,扩展/home/wdp空间 

ln -s  /data  /home/wdp

Linux 给用户 赋某个文件夹操作的权限

# 将目录/data 及其下面的所有文件、子目录的文件主改成 wdp用户权限
chown -R wdp:wdp /data
#给目录opt设置权限
chmod 760 /data

ubuntu samba 权限设置

https://blog.csdn.net/ethan0ly/article/details/83316361

[sam]
    comment = share folder
    path = /home/sam
    create mask = 0777
    directory mask = 0777
    force user = sam
    force group = sam
    available = yes   
    browsable = yes   
    public    = yes
    writable  = yes 

sudo apt-get purge golang-go  go的卸载

sudo apt-get update  更新源
sudo apt-get install package 安装包
sudo apt-get remove package 删除包
sudo apt-cache search package 搜索软件包
sudo apt-cache show package  获取包的相关信息,如说明、大小、版本等
sudo apt-get install package --reinstall  重新安装包
sudo apt-get -f install  修复安装
sudo apt-get remove package --purge 删除包,包括配置文件等
sudo apt-get build-dep package 安装相关的编译环境
sudo apt-get upgrade 更新已安装的包
sudo apt-get dist-upgrade 升级系统
sudo apt-cache depends package 了解使用该包依赖那些包
sudo apt-cache rdepends package 查看该包被哪些包依赖
sudo apt-get source package  下载该包的源代码
sudo apt-get clean && sudo apt-get autoclean 清理无用的包
sudo apt-get check 检查是否有损坏的依赖

 

初次挂载根目录到开发板操作如下

1,pc端搭建nfs服务 
(1)安装及配置nfs服务器

//安装nfs服务器
$ sudo apt-get install nfs-kernel-server
//配置nfs服务,打开/etc/expors 文件
$ sudo vim /etc/exports
//添加如下内容,/home/sam/nfs是我设置的挂载目录,可能你是/home/user(你用户名)/***(自己创建的一个目录)。保存退出,如下图所示
/home/sam/nfs *(rw,sync,insecure,no_root_squash,no_subtree_check)
//重启该服务
$ sudo /etc/init.d/nfs-kernel-server restart

如果想用不同的文件系统,可以改变这个软连接指向新的文件系统,这比复制文件系统或者修改u-boot环境变量方便多了,并且还不用重新配置/etc/exports。

在uboot环境变量中只需设置一次rootpath=/home/user/rootfsln, 每次只需在主机上修改此软连接的指向,即可

$ ln -s /var/rootfs/rootfs_xxx  /home/usr/rootfsln    ## 建立NFS服务目录软连接

 

(2)测试nfs服务搭建是否成功

//挂载我们设置的目录到/mnt文件夹下,若mnt文件夹内容与nfs内容相同表示搭建成功
$ sudo mount 127.0.0.1:/home/sam/nfs  /mnt 

2,uboot设置内核加载文件系统方式为nfs方式 

pc机上右击选择属性-->设备管理->端口->查看USB是几端口

登录开发板在串口   putty应用/Xshell 5点击,选择串口serial   端口输入如上pc查看的USB端口,输入对应的波特率115200 ,若没有反应按开发板上的复位键

先后运行如下三条命令挂载nfs文件系统
(1)uboot设置 根文件系统挂载

设置自动及执行命令。启动延时根据bootdelay的值,若没有设置bootdelay,默认延时3秒

登录开发板在串口中(注意:serial 开发板串口登录按空格键或ESC进入uboot命令行执行如下命令设置(设置开发板IP和根目录环境),若要进入已设置根目录环境则,不用按空格键(如果设置正确)自动进入根目录环境)
进入uboot命令行界面,设置bootargs

set rootpath /home/user/rootfsln      ## NFS服务根目录

set hostname  dm365

 set netdev       eth0                                 ## eth0

set ipaddr        172.4.3.129                 ## 实验板地址

 set serverip     172.4.3.126                 ## 主机地址

set rootpath /home/user/rootfsln      ## NFS服务根目录

setenv bootargs root=/dev/nfs rw nfsroot=$serverip:$rootpath ip=$ipaddr:$serverip:$gatewayip:$netmask:$hostname:$netdev:off console=ttyS0,115200

console=ttyS2,115200n8 //表示设置虚拟终端串口,调试串口。 
root=/dev/nfs //表示指定文件系统位置 
nfsroot=$serverip:$rootpath //表示nfs文件系统挂载位置 

bootargs:启动OS的启动参数

说明:setenv bootargs mem=120M console=ttys0,115200 ...解释:设置传递参数,如内存大小,控制台,根文件系统等。

//根据实际情况修改如上 nfsroot=虚拟机IP地址和nfs安装目录中要挂载的文件系统
 

setenv bootargs 'mem=128M console=ttyAMA0,115200 root=/dev/nfs nfsroot=192.168.5.117:/work/nfs/rootfs ip=192.168.5.197:192.168.5.106:192.168.5.1:255.255.255.255::eth0:off mtdparts=hi_sfc:512k(boot),512K(logo),4M(kernel),9M(rootfs),2M(data)';

(2)bootcmd: 设置启动后自动执行flash中内核起始地址的代码。

set kernaddr    0x23450000     ## flash中内核起始地址

bootm $kernaddr ## 内核镜像uImage在FLASH中的存储地址

说明:setenv bootcmd bootm 0x23450000,设置启动后自动执行0x23450000处的代码。

(3)保存和查看配置

//保存变量及其值到flash.
$ saveenv    
//查看配置,内容如下图所示
$ pri

 

3,完成挂载及测试 
(1)pc端将做好的文件系统复制到我们设置的挂载目录,用于开发板挂载。

(2)重启开发板,内核打印如下信息 

这里写图片描述

 进入到文件系统,如下图所示,可以看到和我们pc端完全一致 

 (3)测试挂载 
在pc端挂载文件夹下touch一个test文件,我们可以在开发板马上看到也有一个tese文件。

4,遇到的问题及解决方法 
VFS: Cannot open root device “nfs” or unknown-block(0,255): error -6 
在/dev目录下面没有nfs,打不开。具体错误如下所示: 

这里写图片描述

 通过资料知道,原来我们内核镜像编译时,没有将nfs挂载文件系统功能编译进去,内核根本不支持nfs操作,所以造成如上错误。 
遇到问题就要解决啊,我们就编译一个有支持nfs挂载文件系统的内核镜像。我们通过make menuconfig 配置使其支持此功能。 
(1)进入文件系统配置 

 (2)启动网络文件系统功能 

这里写图片描述

 (3)开启nfs功能 

这里写图片描述

(4)网络配置,启动Networking support 

这里写图片描述

5)进入网络支持,里面的Networking options(网络配置选项) 

这里写图片描述

 (6)保存配置,重新编译内核

 

前已经初次挂载根目录到开发板后,下次再次挂载根目录到开发板操作步骤如下

1)虚拟机异常关机(或重启系统)后,需要重启nfs服务/etc/init.d/nfs-kernel-server restart

2)下次通过 串口  登陆开发板需要重新按开发板的复位键

备注:192.168.5.117:/work/nfs/rootfs虚拟机192.168.5.117下/work/nfs中rootfs根文件系统下的内容挂载到开发板根目录/下

遇到问题 通过串口终端无法登陆连接串口,串口是否被占用,解决方法:拔pc端串口重插或在设备管理器-〉操作-〉扫描检测硬件改动或重启Pc系统

          换个登录终端,是否usb转串口连错或坏了

 

来检测U盘插拔,要用dbus。
 

 

参考:

Linux调试分析诊断利器——strace  https://www.cnblogs.com/clover-toeic/p/3738156.html

如何验证 ulimit 中的资源限制?如何查看当前使用量?https://feichashao.com/ulimit_demo/

如何检测内存泄漏?https://www.kutu66.com//ubuntu/article_157963

https://elinux.org/Memory_Debuggers

Linux下几款C++程序中的内存泄露检查工具https://blog.csdn.net/gatieme/article/details/51959654

Valgrind ---内存调试,内存泄漏检测以及性能分析的软件开发工具 https://blog.csdn.net/vevenlcf/article/details/22875585

利用linux的mtrace命令定位内存泄露(Memory Leak) https://www.cnblogs.com/yangykaifa/p/7397497.htmldmalloc用法快速入门

dmalloc用法快速入门 https://blog.csdn.net/ohhmygod/article/details/17057343

学习linux命令,看这篇2w多字的命令详解就够了 https://mp.weixin.qq.com/s/7bSwKiPmtJbs7FtRWZZqpA

常用的分析ELF文件的命令(readelf、objdump及od)https://blog.csdn.net/qq_21331015/article/details/103210449


版权声明:本文为sam2009944096原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。