- 浏览: 430947 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
lkun__blog:
网页打不开啊
博客搬家到http://cuiz.me -
bglmmz:
楼主怎么解决的?我用python调用ice服务,也出现此问题, ...
syscall exception: 存储空间不足,无法处理此命令 -
luliangy:
哥,你什么配置,我10W个请求10秒左右就搞定了,毫无压力,R ...
Nginx和Apache简单的并发压力测试 -
liuxuejin:
这!看的我都···········。我看代码而已。怎么
EPOLL及消息队列实现SMTP 之 青楼的故事 -
zires:
night_stalker 写道unicorn 也很好维护啊, ...
Unicorn和Passenger性能测试对比
我想更深入的了解Ruby内部的实现,出发点或许过于天真,
我想了解下这门语言的实现,从中或许可以学习到某些思路,
比如:
- 如果我们要设计另外一种动态语言该如何去下手,
- 如何将其他语言的特性融合进Ruby或者我们要设计的语言,
- 特定领域的特定语言该如何设计(不要一门又广又全的语言,但又不是DSL)。
题目是《从main.c开始走进Ruby》,那我们需要以下的准备工作
- Ruby的源码,我采用的是ruby-1.9.2,
- gdb(神器)
- 一个IDE,eclipse(可选)
得到源码之后,首先要对其进行编译,不能采用默认的编译方式,
要在执行./configure的时候加上如下参数
- CFLAGS="-ggdb -g3 -gdwarf-2"
或者在Makefile中添加如下标志参数:
- optflags = -O
- debugflags = -ggdb -g3 -gdwarf-2
这样编译的Ruby包含了充足的调试信息,同时还保留了Ruby源码中大量使用的宏信息,
比如我们在gdb中显示如下的宏:
(gdb) info macro RTEST Defined at ./include/ruby/ruby.h:349 included at /home/zheng.cuizh/ruby-1.9.2-rc2/ruby.c:18 #define RTEST(v) (((VALUE)(v) & ~Qnil) != 0)
如果编译参数没有添加,那么编译出来的代码是没办法看到macro信息的。
在编译好Ruby后,需要使用make install安装,安装好后就可以登上调试Ruby之旅了。
调试Ruby有几种方法,只要能在你知道的地方下个断点,并且Ruby解析器能够执行到该断点即可,
我想到以下两种方式:
- ruby -e "h=Hash.new"
- irb
对于第一种方式,我们直接使用gdb ruby启动即可,然后在gdb的console中设置属性及断点:
- gdb ruby
- set args -e "h=Hash.new"
- b main
对于第二种方式,我们先执行irb,然后再开启一个终端,通过ps ax|grep irb|grep -v grep|awk '{print $1}' 找到irb的进程id,然后用gdb attach到该进程即可:
- irb
- gdb - <irb_pid>
- b rb_hash_initialize
- c
- h=Hash.new#irb中输入
这样的话就会在初始化Hash对象的时候被断到,然后就可以继续调试了。
个人推荐采用第二种方式来调试Ruby解析器。
这里我要多说一下,第一种方式的最后一步b main的意思是对main()这个位于main.c中的入口函数。
(gdb) list main
18
19 RUBY_GLOBAL_SETUP
20
21 int
22 main(int argc, char **argv)
23 {
24 #省略
30
31 ruby_sysinit(&argc, &argv);
32 {
33 RUBY_INIT_STACK;
34 ruby_init();
35 return ruby_run_node(ruby_options(argc, argv));
36 }
37 }
(gdb)
Line number 38 out of range; main.c has 37 lines.
Ruby是一种解释型语言,它的代码都是没编译过的字符串,
- 在Ruby解析器执行起来后,
vm_exec(th);
- 通过读入Ruby代码文件,动态解析
PREPARE_PARSE_MAIN({
tree = load_file(parser, opt->script, 1, opt);
});
从而完成边解析边执行的过程。第一种方法的main.c函数就是ruby解析器的入口,当我们在那里下断点后,必定能将Ruby断下来。
在main.c35行这句话:return ruby_run_node(ruby_options(argc, argv));
我们需要在gdb里面通过键入s跟进两次,ruby_run_node和ruby_options这两个函数都需要步入,他们都很重要。其中ruby_run_node会调用vm_exec来执行语法树。
至此,我们的准备工作就完成了。很简单的步骤,却很重要。
之后的工作就是不断的跟踪Ruby各种类型的实现机制,简单的描述就是在irb里面不停尝试各种类型的new操作,同时在gdb里面不断的对不同类型rb_object_initialize下断点,然后步入每一个想知道的方法中,对某些变量设置观察点,打印出结构体,再步进。
真感谢Ruby有个irb这么好用的工具,也是由于动态语言的缘故,代码可以通过eval来执行。
有了irb,我们就可以交互式调试没段代码了。
这是走进Ruby的开始,我也刚刚做好准备工作,很多Ruby内部的实现我还没有读懂,我边读边写,不过我要在我读懂了之后再写出来,由于悟性的问题,我可能写的很慢或者有错误的地方,而且思路可能还会很跳跃^-^,希望高手能指点一下。
Ruby有好多预定以变量,多的让我不可能记住,这里有个网页,记录了和Ruby有关的一些关键字:
http://www.tutorialspoint.com/ruby/ruby_predefined_constants.htm
我们在使用Ruby的argv参数数组时,知道$0是当前进程的程序名称,
比如我们在启动的irb中执行$0,就会返回"irb",
这个$0是在这里被保存的:
rb_argv0 = rb_str_new4(rb_progname);
rb_progname是什么,debug跟踪进去可以看到如下定义:
#define rb_progname (GET_VM()->progname) #define GET_VM() ruby_current_vm
这俩段宏可以得到当前Ruby解析器的名字,这个名字是保存在下面这个结构体中的,
下面这个是ruby vm_core.h中的解析器结构体,
typedef struct rb_vm_struct { VALUE self; rb_thread_lock_t global_vm_lock; struct rb_thread_struct *main_thread; struct rb_thread_struct *running_thread; st_table *living_threads; VALUE thgroup_default; int running; int thread_abort_on_exception; unsigned long trace_flag; volatile int sleeper; /* object management */ VALUE mark_object_ary; VALUE special_exceptions[ruby_special_error_count]; /* load */ VALUE top_self; VALUE load_path; VALUE loaded_features; struct st_table *loading_table; /* signal */ struct { VALUE cmd; int safe; } trap_list[RUBY_NSIG]; /* hook */ rb_event_hook_t *event_hooks; int src_encoding_index; VALUE verbose, debug, progname; VALUE coverages; struct unlinked_method_entry_list_entry *unlinked_method_entry_list; #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE struct rb_objspace *objspace; #endif } rb_vm_t;
和python等其他脚本语言类似,脚本语言无法真实的实现native thread来做并行运算,可以通过下面这个成员看出端倪:
全局解释器锁
rb_thread_lock_t global_vm_lock;
在调试过程中要善用bt这个命令,通过bt我们可以看到某个函数的调用栈,配合eclipse等IDE(没有IDE的话就用layout查看代码),可以很好的分析出代码的走向,一个实例创建的过程。
比如在以irb方式调试的时候,刚刚断入irb的进程,执行bt后看如下输出:
(gdb) bt
#0 0x000000304360d5cb in read () from /lib64/libpthread.so.0
#1 0x0000000000527803 in rb_thread_blocking_region (func=0x42b0e0 <internal_read_func>, data1=0x7fff42460860, ubf=0x527960 <ubf_select>, data2=0xb58bf0) at thread.c:1117
#2 0x000000000042bae8 in io_fillbuf (fptr=0xe88a40) at io.c:587
#3 0x0000000000437004 in rb_io_getbyte (io=<value optimized out>) at io.c:3101
#4 0x0000000000517ee5 in vm_call0 (th=0xb58bf0, recv=15222400, id=1928, argc=0, argv=0x0, me=0xc28600) at vm_eval.c:78
#5 0x000000000051b27b in rb_funcall (recv=15222400, mid=0, n=0) at vm_eval.c:234
#6 0x00002aaaaaeb29bd in readline_getc (input=0x3042d516a0) at readline.c:120
#7 0x0000003043e25eba in rl_read_key () from /usr/lib64/libreadline.so.5
#8 0x0000003043e14921 in readline_internal_char () from /usr/lib64/libreadline.so.5
#9 0x0000003043e14d65 in readline () from /usr/lib64/libreadline.so.5
#10 0x000000000041773d in rb_protect (proc=0x2aaaaaeb2940 <readline_get>, data=15701216, state=0x7fff42460e6c) at eval.c:718
#11 0x00002aaaaaeb28ae in readline_readline (argc=2, argv=<value optimized out>, self=<value optimized out>) at readline.c:255
#12 0x000000000050cc95 in vm_call_method (th=0xb58bf0, cfp=0x2af8e53e16c8, num=2, blockptr=0x1, flag=8, id=0, me=0xe217b0, recv=15222560) at vm_insnhelper.c:401
#13 0x00000000005103b7 in vm_exec_core (th=0xb58bf0, initial=<value optimized out>) at insns.def:1006
#14 0x0000000000516c99 in vm_exec (th=0x0) at vm.c:1145
#15 0x000000000051c3c8 in invoke_block_from_c (th=0xb58bf0, block=0xe9d9c0, self=15225000, argc=0, argv=0x0, blockptr=0x0, cref=0x0) at vm.c:557
#16 0x000000000051c77f in rb_vm_invoke_proc (th=0xb58bf0, proc=0xe9d9c0, self=15225000, argc=0, argv=0x2af8e52e2250, blockptr=0x0) at vm.c:603
#17 0x000000000050cc95 in vm_call_method (th=0xb58bf0, cfp=0x2af8e53e18d8, num=0, blockptr=0x1, flag=0, id=0, me=0xc52000, recv=15307040) at vm_insnhelper.c:401
#18 0x00000000005103b7 in vm_exec_core (th=0xb58bf0, initial=<value optimized out>) at insns.def:1006
#19 0x0000000000516c99 in vm_exec (th=0x0) at vm.c:1145
#20 0x000000000051c3c8 in invoke_block_from_c (th=0xb58bf0, block=0x2af8e53e1c18, self=15213640, argc=0, argv=0x0, blockptr=0x0, cref=0x0) at vm.c:557
#21 0x000000000051c888 in loop_i () at vm.c:587
#22 0x00000000004179a9 in rb_rescue2 (b_proc=0x51c850 <loop_i>, data1=0, r_proc=0, data2=0) at eval.c:646
#23 0x00000000005092e9 in rb_f_loop (self=15213640) at vm_eval.c:816
#24 0x000000000050cc95 in vm_call_method (th=0xb58bf0, cfp=0x2af8e53e1bf0, num=0, blockptr=0x2af8e53e1c19, flag=8, id=0, me=0xbe21a0, recv=15213640) at vm_insnhelper.c:401
#25 0x00000000005103b7 in vm_exec_core (th=0xb58bf0, initial=<value optimized out>) at insns.def:1006
#26 0x0000000000516c99 in vm_exec (th=0x0) at vm.c:1145
#27 0x000000000051c3c8 in invoke_block_from_c (th=0xb58bf0, block=0x2af8e53e1d20, self=15213640, argc=1, argv=0x0, blockptr=0x0, cref=0x0) at vm.c:557
#28 0x000000000051c82e in catch_i (tag=4080910, data=<value optimized out>) at vm.c:587
#29 0x00000000005084fd in rb_catch_obj (tag=4080910, func=0x51c7f0 <catch_i>, data=0) at vm_eval.c:1522
#30 0x0000000000509198 in rb_f_catch (argc=<value optimized out>, argv=<value optimized out>) at vm_eval.c:1498
#31 0x000000000050cc95 in vm_call_method (th=0xb58bf0, cfp=0x2af8e53e1cf8, num=1, blockptr=0x2af8e53e1d21, flag=8, id=0, me=0xbe1e20, recv=15213640) at vm_insnhelper.c:401
#32 0x00000000005103b7 in vm_exec_core (th=0xb58bf0, initial=<value optimized out>) at insns.def:1006
#33 0x0000000000516c99 in vm_exec (th=0x0) at vm.c:1145
#34 0x000000000051c3c8 in invoke_block_from_c (th=0xb58bf0, block=0x2af8e53e1ed8, self=12129600, argc=1, argv=0x0, blockptr=0x0, cref=0x0) at vm.c:557
#35 0x000000000051c82e in catch_i (tag=3247374, data=<value optimized out>) at vm.c:587
#36 0x00000000005084fd in rb_catch_obj (tag=3247374, func=0x51c7f0 <catch_i>, data=0) at vm_eval.c:1522
#37 0x0000000000509198 in rb_f_catch (argc=<value optimized out>, argv=<value optimized out>) at vm_eval.c:1498
---Type <return> to continue, or q <return> to quit---
#38 0x000000000050cc95 in vm_call_method (th=0xb58bf0, cfp=0x2af8e53e1eb0, num=1, blockptr=0x2af8e53e1ed9, flag=8, id=0, me=0xbe1e20, recv=12129600) at vm_insnhelper.c:401
#39 0x00000000005103b7 in vm_exec_core (th=0xb58bf0, initial=<value optimized out>) at insns.def:1006
#40 0x0000000000516c99 in vm_exec (th=0x0) at vm.c:1145
#41 0x0000000000516f74 in rb_iseq_eval_main (iseqval=12097560) at vm.c:1386
#42 0x0000000000417bcf in ruby_exec_internal (n=0xb89818) at eval.c:214
#43 0x000000000041a396 in ruby_run_node (n=<value optimized out>) at eval.c:261
#44 0x0000000000416e5d in main (argc=2, argv=0x7fff424632c8) at main.c:35
第一行read函数就是irb和使用者交互的方法,
最后一行的main就是Ruby解析器的入口函数,
从下到上就是Ruby解析器整个执行过程。
不过我还没弄明白loop_i这个函数的作用。
先写到这里,下一章我也不知道会写什么,因为前提是我看懂了什么^-^
评论
喜欢捣鼓gdb和窥探ruby内部的朋友,这位大牛的blog千万不能错过:http://timetobleed.com/
他一篇讲BUG的配图太恶心了
gdb也行阿,list就可以看到当前执行到的上下文,layout更可视化,就是个
vim了.
不过可读性没有带UI的调试器好,不过为了可读性,我建议大家装个IDE么,就是为了看代码,跳转来,跳转去.
喜欢捣鼓gdb和窥探ruby内部的朋友,这位大牛的blog千万不能错过:http://timetobleed.com/
好,我去偷艺:)
喜欢捣鼓gdb和窥探ruby内部的朋友,这位大牛的blog千万不能错过:http://timetobleed.com/
加油,这系列有潜力~
光一个new能看到的东西就已经相当多了,包括GC检查剩余可用空间/触发收集等也在流程中:
static void * vm_xmalloc(rb_objspace_t *objspace, size_t size) { void *mem; if ((ssize_t)size < 0) { negative_size_allocation_error("negative allocation size (or too big)"); } if (size == 0) size = 1; #if CALC_EXACT_MALLOC_SIZE size += sizeof(size_t); #endif if ((ruby_gc_stress && !ruby_disable_gc_stress) || (malloc_increase+size) > malloc_limit) { garbage_collect_with_gvl(objspace); } mem = malloc(size); // 尝试分配空间 if (!mem) { // 如果没分配到空间的话, if (garbage_collect_with_gvl(objspace)) { // 就触发一次收集 mem = malloc(size); // 收集过后再试一次分配空间 } if (!mem) { ruby_memerror(); } } malloc_increase += size; #if CALC_EXACT_MALLOC_SIZE objspace->malloc_params.allocated_size += size; objspace->malloc_params.allocations++; ((size_t *)mem)[0] = size; mem = (size_t *)mem + 1; #endif return mem; }
我也很希望能持续的写下去,但途中肯定会遇到一些问题的,到时候我会把问题抛出来,还请大家不要吝啬各自的才华帮我看看问题,借助大家的帮忙估计还是能写几篇的,希望大家不吝赐教啊!
发表评论
-
N度空间关系图
2011-05-11 17:22 1604计算机 ... -
Rails3和Rack依赖关系图
2011-05-11 17:17 0找到一个好的画图工具不容易啊,试试这个 -
Unicorn和Passenger性能测试对比
2011-05-03 13:04 4355测试工具:ab 测试 ... -
libsmtp--库的一个bug
2011-02-18 17:09 1886http://libsmtp.sourceforge.net/ ... -
从main.c开始走进Ruby-异常
2010-08-26 18:21 1191这一阵子真没时间,9月上旬更没时间,头大. 前天写面试题目的 ... -
从main.c开始走进Ruby-有形亦无形的数据
2010-08-20 00:32 2312上一篇文章我们找到了如何调试Ruby的入口,只要走进去, ... -
日积月累-分享我的工具库
2010-08-04 11:53 1294批量替换 指定目录及其子目录中所有文件内的字符串 ... -
关键字和预定义变量:__END__和DATA的问题
2010-05-13 10:07 1329两个文件,a.rb和b.rb 当a.r ... -
[J]Ruby自编译安装
2010-02-24 11:37 1351#直接Copy并粘贴到控制台 #安装Ruby1.9.1- ... -
libvirt和ruby-libvirt在Macos系统上安装失败解决方法
2010-01-22 17:31 1193附件中是补丁及安装脚本, 安装前先看下install那个脚本 ... -
【GUI】LoadRunner的Controller定时执行
2010-01-19 14:38 3730玩玩的,很好玩不是么,工作就是要好玩,否则还工作个屁啊。 ... -
将QC的COM接口开放成Rest服务[续]
2010-01-14 11:53 1552利用QC的开放架构平台的COM组建, 给HP的QC写一个Met ... -
Ruby的ActiveRecord1.9个小时能够插入1000万mysql数据
2009-12-29 15:38 950rtrtrt -
TextMate中Command+R无法执行的变通解决方法
2009-12-28 12:08 2762如果你在升级了雪豹并且设定为64位启动模式后, TM无法通过C ... -
批量更改主机密码
2009-12-22 17:00 804require "rubygems" r ... -
将QC的COM接口开放成Rest服务
2009-11-16 17:05 2433以Ruby代码为例, QC平台的SDK以COM组件的形式对 ... -
虚拟机!
2009-11-12 16:25 844ruby-libvirt ============ Ruby ... -
Swig编译C/C++代码给Ruby [on Mac]
2009-10-12 15:01 2143charlesdemacbook-pro:swig Cui$ ... -
让一个类include一个模块的几种方法
2009-09-22 10:59 827module Test module ClassMeth ... -
ruby中当前系统分隔符
2009-09-08 15:28 819$/
相关推荐
| | |-- main.c | | |-- Makefile | | |-- nand.c | | |-- s3c24xx.h | | |-- serial.c | | `-- serial.h | |-- hello | | `-- hello.c | |-- i2c | | |-- head.S | | |-- i2c.c | | |-- i2c.h | | |-- i2c.lds | | ...
You'll need a recent (2.6+) version of Ruby, but that's it. Minitest ships with the language, so you're all set. Anatomy of an Exercise The files for an exercise live in exercises/<slug>. The slug ...
2004-07-16 13:16 24036 2415 BusinessSkinForm\Ampix\main.bmp 2004-07-09 21:54 3422 91 BusinessSkinForm\Ampix\mask.bmp 2004-07-16 13:17 8912 1183 BusinessSkinForm\Ampix\menuitems.bmp 2004-07-16 13:17 ...
编译原理 C语言编译器(包括词法/语法/语义分析器等) 项目结构如下 -source --lexAnalysis 词法分析器(原创) ---analyse.c 词法分析器 ---text.c 测试用例(被分析的C代码) --lexSynAnalysis 语法分析器(转存...
首先,Ruby的面向对象特性是其核心之一。在Ruby中,一切都是对象,包括基本数据类型如数字、字符串和布尔值。例如,当你在Ruby中写下"hello",它实际上是一个String对象,你可以调用方法在它上面操作,如`"hello"....
main.c Makefile.multi-target.template print_int_array.c.noindent .vim/c-support/doc: ChangeLog c-hotkeys.pdf c-hotkeys.tex .vim/c-support/rc: customization.ctags customization.gvimrc customization....
【rtf文件用windows的写字板打开较好】 des.rar 2009-11-01 07:47 45056 10080 ...2009-11-08 06:41 822 416 des\main.cpp.rtf 2010-09-18 02:23 文件夹 文件夹 des # # 总计 大小 压缩后大小 文件数 # 532147 196928 7
The main directory where all μC/OS-II files are located. \SOFTWARE\uCOS-II\EX1_x86L This directory contains the source code for EXAMPLE #1 (see section 1.07, Example #1) which is intended to run ...
mysqlWorkbench 汉化
用法Ruby用法示例: $ lsGemfile Gemfile.lock$ heroku create --buildpack heroku/ruby$ git push heroku main...-----> Heroku receiving push-----> Fetching custom buildpack-----> Ruby app detected...
为了简化这一过程,开发者们创造了各种工具,其中之一就是"Main"库。Main是一个Ruby类工厂和领域特定语言(DSL),专门设计用于快速、简洁地生成命令行程序。 "Main"库的核心理念是通过提供一种结构化的方式来定义...
gcc common.c main.c menu.c guide.c game.c -o RUN -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf 2. 使用Clion编译 请注意文件夹中的```CMakeLists.txt``` 在编译时,请点开**编辑配置**窗口,将**工作目录改为本...
c语言程序,调用cmd运行当前目录下名为main.pyw或main.py的文件
哈夫曼编码实现_c语言 (最小堆) 求WPL -----递归求解
这是因为`m1`中的`main`函数实际上从`0x5589`地址开始执行,这是通常的栈帧布局的一部分,其中`%ebp`寄存器的初始值。 理解这些概念对于编写和调试多文件C程序至关重要,特别是在处理全局变量和函数时,以及在模块...
《PC蓝牙调试工具——BleWinrtDll-main.zip详解》 在现代的计算机技术中,蓝牙功能已经成为设备间通信的重要方式之一。对于开发者而言,理解并掌握蓝牙的调试工具至关重要,尤其是在开发基于Windows平台的蓝牙应用...
Ruby-Linters 在这个项目中,我...Linters-Capstone文件夹中$ cd Ruby-Linters-Capstone$ ruby bin/main.rb test.rb用您要检查的任何文件更改test.rb好与坏的例子缩进好例子class Test def initialize(name) @name =
在Ruby编程语言中,创建命令行接口(CLI)工具是一种非常实用的方法,它可以使开发者能够快速构建具有交互功能的工具,方便日常任务自动化或者提供特定服务。Ruby的灵活性和强大的库支持使得构建CLI变得简单易行。...
### Linux操作系统下C语言编程知识点详解 #### 一、基础知识 **1.1 源程序编译** 在Linux环境下,使用C语言编程时最常用的编译器是GNU的`gcc`(GNU Compiler Collection)。`gcc`是一个强大的编译器,能够支持...