Tuesday, July 21, 2009

[Linux]GDB笔记

本文参考了如下资源
GDB quick reference: 一个很不错的cheatsheet
RMS的gdb tutorial
中文的gdb教程

1 最基本用法

最基本的gdb用法可以帮助你找到程序里哪里出现segment fault。首先你要在gcc编译的时候加上-ggdb选项。假定你的程序名叫foo,你可以用如下命令进入gdb开始program进行debug:
$ gdb foo
gdb会在你出现段错误的地方停下来,你会看到是哪个程序哪行语句出了问题.
2 Core Dump

发现了segment fault的话,还可以使用core dump。这样不但可以很方便的找到出问题的代码. 还可以多次重现这个错误。 首先使用以下的命令允许系统生成core文件.
$ ulimit -c unlimited
然后运行你的代码,比如叫foo, 等到segment fault发生并异常退出以后, 你会发现在foo的同一个文件夹下多了一个名字类似core.1234这样的文件。这时候运行如下命令就可以重现案发现场了
$ gdb foo core.1234

3 普通用法

r [arglist]: (重新)运行程序
b [file:]line, b [file:]function: 设置断点在line或者function
c, continue: 继续运行程序
s, step: 单步执行, 进入函数
n, next: 单步执行, 不进入函数
finish: 执行完当前frame
kill: 终止当前程序
q, Ctrl-d: 退出gdb
p [expr]: 显示表达式expr. 比如var表示变量var的内容而 &var表示变量var的内存地址
bt: 显示当前调用栈(call stack)
set args [arglist]: 设置命令行参数,从而可以直接用r(run)来执行

4 断点(breakpoit)/观察点(watchpoint)设置与管理

b [file:]line, b [file:]function: 设置断点在line或者function
watch [expr]: 设置观察点,一旦表达式expr有变化就停下来. expr通常为一个变量或者内存地址
rwatch [expr]: 设置观察点,一旦被读就停下程序
awatch [expr]: 设置观察点,一旦被读或者被写都停下程序
比如你可以使用awatch *(long*)addr来监视内存地址addr是否被读或者被写
info breakpoints/watchpoints: 查看当前设置了的断点和观察点, info watchpoints是info breakpoints的alias
enable/disable/delete [n]: 激活/禁用/删除 编号为[n]的断点/观察点

5 程序栈

bt, backtrace: 显示当前调用栈(call stack),比如
(gdb) bt
#0  0x000000010005ea07 in init (ctx=0x100622070, opts=@0x10060d078) at Utils.cpp:1678
#1  0x00000001000149ad in main (argc=3, argv=0x7fff5fbffb30) at main.cpp:591
frame: 改变frame,比如
(gdb) frame 1
#2  0x8048414 in main (argc=1, argv=0xbffffaf4) at test.c:19
19        x = func1(x);
info frame: 查看当前frame的信息
(gdb) info frame
Stack level 2, frame at 0xbffffa8c:
eip = 0x8048414 in main (test.c:19); saved eip 0x40037f5c
called by frame at 0xbffffac8, caller of frame at 0xbffffa5c 
source language c.
Arglist at 0xbffffa8c, args: argc=1, argv=0xbffffaf4
Locals at 0xbffffa8c, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffffa8c, eip at 0xbffffa90
info locals: 查看当前frame里的局部变量
(gdb) info locals
x = 30
s = 0x8048484 "Hello World!\n"
info args: 查看当前frame的调用参数
(gdb) info args
argc = 1
argv = (char **) 0xbffffaf4


6 查看变量/函数, 改变变量值

如果有一个指针变量it指向一个struct item, 查看这个struct item的内存地址:
(gdb) p it
$1 = (item *) 0x100700060
查看这个struct的内容
(gdb) p *it
$2 = {
  next = 0x100700000, 
  prev = 0x0
}
如果你有如下数组
int *array = (int *) malloc (len * sizeof (int));
你可以这样来查看你的数组
p *array@len
有时候你知道一段二进制代码所属的函数和偏移, 查这段对应的源代码:
list *myfunc+0x16

改变一个变量a的值为10
set variable a=10

7 查看内存

用命令"x /nfu <addr>"
n表示要显示的内存单元的个数
f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。
u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节

8 其他


info source: 查看当前frame对应的源文件信息

(gdb) info source
Current source file is shared_ptr.hpp
Compilation directory is /usr/local/include/boost/smart_ptr
Located in /usr/local/include/boost/smart_ptr/shared_ptr.hpp
Contains 712 lines.
Source language is c++.
Compiled with unknown debugging format.
Does not include preprocessor macro info.
info threads: 查看当前线程数目以及状态

(gdb) info threads
       Id   Target Id         Frame
       3    process 35 thread 27  0x34e5 in sigpause ()
       2    process 35 thread 23  0x34e5 in sigpause ()
     * 1    process 35 thread 13  main (argc=1, argv=0x7ffffff8)
         at threadtest.c:68

Friday, July 10, 2009

[数学]三个囚徒的问题

从mitbbs上看到这道题,并查了wiki,觉得挺有意思的. 特别是两个版本细微的差别就会导致结果的不一样.

版本1
一个国王决定从3个囚徒A,B以及C中释放一个,处决另外两个.国王告诉A说,处决的犯人中不是A的那个(如果两个都不是A,则随机说一个)是B.A在得知这个信息以后,他被处决的概率是多少?
SOL: 1/3

版本2
一个国王决定从3个囚徒A,B以及C中释放一个,处决另外两个.国王告诉A说,处决的犯人包括B.A在得知这个信息以后,他被处决的概率是多少?
SOL: 1/2

用列举法来解释结果:
国王的决定 版本1国王告诉A版本1可以排除掉版本2可以排除掉
放A 杀B 杀C B   
放A 杀B 杀CCx  
杀A 放B 杀C Cxx
杀A 放B 杀CCxx
杀A 杀B 放CB  
杀A 杀B 放CB  

Thursday, July 09, 2009

[TeX]关于Tex, Latex,以及**tex的一个小结

叫**TeX的东西太多了,我自己对它们的概念也很混淆.所以最近花了一些时间整理一下相关内容,作为笔记加深自己的理解.
名词解释:其实这里有一个很不错的TeX名词的分类解释:TeX Catalogue
  • TeX: 所有**tex的源头,万物的本源. 它由Knuth爷爷用他自己钟爱的一种和Pascal很像的叫WEB的语言所编写的.由于年代的缘故,它有这样那样的局限性. 比如那个时候Knuth爷爷是为了给自己的巨著TAOCP(被视为Computer Science的圣经)排版所用,因此只考虑了支持英文.所以其输入必须是ascii编码的文本文件,而不能包括多字节的语言比如中文.再比如TeX的输出结果为dvi文件. dvi是"device independent format"的缩写.它使用点阵字体而不是轮廓字体.而点阵字体只有在特定大小的时候才最好看.
    Knuth爷爷拒绝对TeX再做改动,所以人们就对TeX作了各种各样的扩展.
  • pdfTeX: 这是对Knuth的TeX的扩展,用于直接输出为pdf而不是dvi(TeX的输出).除此以外相对于TeX,它可以支持TrueType以及Type 1等轮廓字体.
  • XeTeX: 对TeX最大的改进体现在支持unicode以及OpenType等现代字体. 因此对中文的输入的天然支持的.它的工作过程分为两步:第一步生成extended dvi文件(xdv);第二步再将xdv文件转为pdf文件. xelatex是使用XeTeX引擎来编译latex源文件的工具.
  • luaTeX: 基于pdfTeX和Lua语言的TeX引擎. 同样也支持unicode.
  • Primitive Tex: TeX原始命令.大约有300多个. 有兴趣的可以看一下这里 Primitive Tex.由于它非常的底层,所以一般人不会直接用它,而是用在它基础上的各种宏语言.
  • plain TeX: Knuth自己创建的一种建立在Primitive TeX上的宏语言.大约有600多个命令.
  • 我们来看一下用plain TeX所写的Hello World:
    Hello, World
    \bye
  • ConTeX: TeX的另一种宏包.比如这是ConTeX 写的Hello World例子
    \starttext
    Hello, World
    \stoptext
    你可以在http://live.contextgarden.net/尝试在线编译ConTeX文件
  • LaTeX: TeX的最为著名的一种宏包. LaTeX也是大有来头,它是由Leslie Lamport最初弄出来的.对于一般使用者,它比Knuth自己的plain TeX更加直观方便.版面配置的工作被相当大程度的减轻了,使用者可以更加专注在内容上.因此大获流行,比如很多(还是大多?)学术期刊或者会议都会prefer你用LaTeX来排版提交文稿.
    既然是宏语言,就实际上可以被替换成TeX可以理解的命令.同时你也可以在LaTeX文档中使用Primitive TeX的命令.LaTeX宏的格式定义在latex.fmt这个文件当中.遗憾的是它是一个二进制文件,无法直接阅读. 但是幸运的是我们可以从latex.ltx这个文件中看到我们所熟知的各种环境,比如tabular eqnarray等等.实际上latex.fmt正是从latex.ltx编译而来.
    LaTeX定义的标准命令标准环境
    我们再来看一下用LaTeX写的Hello World程序.
    \documentclass{article}
    \begin{document}
    Hello, World
    \end{document}
  • LaTeX distribution:为了用LaTeX来排版,你需要一系列工具,包括(1)编辑器(比如我用过vi, WinEdt, editplus, textwrangler,gedit,现在主要用emacs)(2)dvi浏览器(或者pdf浏览器)(3)最重要的:一套LaTeX发行.一个LaTeX发行通常要包括如下的程序:
    • tex: 把TeX 文件编译成DVI文件
    • pdftex: 把TeX 文件编译成PDF文件
    • latex: 最常用, 把LaTeX文件编译成DVI文件
    • pdflatex: 把LaTeX编译成PDF文件
    • dvi2ps: 把DVI文件转成PostScript文件
    • dvipdf: 把DVI文件转成PDF文件
    • dvipdfm: dvipdf的改进版本
  • TeX live: LaTeX的一个跨平台的发行版, Win,Mac,Linux等主要平台上都有.
  • MiKTeX: LaTeX在Windows平台上的发行版.在中国大家使用非常广泛的CTeX套装里就使用的是MiKTeX.