DDD もどき

休みを利用して、浅草から出ている屋形船に乗ってきた。浅草から隅田川を下って、お台場 のレインボーブリッジをくぐり、全部で16の橋の下を 通ると言う全2時間のコース。食べ放題、飲み放題で8000円。それだけの価値は ありましたねぇ。

船遊びなんて、何年ぶりだろう? 隅田川と友好河川になっていると言うセーヌ川は知らない けれど、ライン川とか、アムステルダムの運河めぐり以来かな。

東京があんなに水の豊かな所だったなんて、認識を新たにしましたよ。川と言えども適当に 船が揺れてくれたおかげで、ビールが回るのも心なしか早い。揚げたての江戸前てんぷらが おいしかった。外人さんには、六本木に行くより、浅草でも巡って、船遊びをお勧めしたいね。

両岸に立ち並ぶビル、面白かった。住友のツインビルとか、リバーシティ21とか、バンダイ とか聖路加とか、そして月島のあのビルは、あの方のお住まいがあるのかな。SICPがらみで 高みに登ってみたいものだ。

そして、今押上に作っているスカイツリーが完成したら、タワーの見物がてら、神谷バー経由で 水上バスにでも乗って、フジテレビ方面にでも行きたいな。

DDDって、壊れてる?

前回、ArchLinuxに入れたDDD、配列をグラフ表示させようとすると、Starting gnuplotと 表示が出たままになってしまい、何も表示されないんだ。ArchLinuxの担当者が下手こいた かと思って、FreeBSDでもやってみた。

debugにかけられるターゲットは、こんな他愛もないやつね。

     1  #include <stdio.h>
     2  #include <math.h>
     3
     4  #define SIZE 4096
     5  double r[SIZE];
     6
     7  main (){
     8     int i;
     9
    10     for (i = 0; i < SIZE; i++){
    11        r[i] = sqrt(i);
    12     }
    13     for (i = 0; i < SIZE; i++){
    14        printf("%d %f\n", i, r[i] );
    15     }
    16  }

cc -g test.c -lm こういう風にコンパイルして、a.outを作る。そして、ddd a.outして DDDを起動する。

GNU DDD 3.3.12 (i386-portbld-freebsd8.0), by Dorothea L(gdb) break test.c:13
Breakpoint 1 at 0x8048446: file test.c, line 13.
(gdb) run

Breakpoint 1, main () at test.c:13
(gdb) graph plot r
(gdb)

13行目にブレークポイントを置いて実行。止まったら、配列 r には、INDEXの平方根が 充填されてるはず。rをマウスでなぞる(反転)と、plotと言うボタンが有効になるので 押す。 操作はこれでいいはずなんだけど。。グラフが出てこないんだ。

Starting gnuplot... が、つれなく出てくるだけ。 こうならないんだよなあ。グラフ表示は、gnuplotへ 下請け(嗚呼、IT風に言うと、アウトソーシングかな)に出しているんだな。そうすると、 下請けが働いているか、まず査察するか。

 6787   2  S+     0:00.67 ddd a.out
 6788   3  Is+    0:00.09 gdb -q -fullname a.out
 6789   3  TX     0:00.01 /usr/home/sakae/ddd/a.out
 6790   2  I+     0:00.03 gnuplot -bg grey96 -font -*-lucidatypewriter-medium-r

これ見ると、動いてるみたいだよ。なんで結果が表示されない? LinuxでもFreeBSDでも 動かんと言う事は、本社側(DDD)が腐っているのかなあ。現実にはよくあるし。。。

世界のみんなに評判を聞いてみるかな。ddd ”Starting gnuplot”で、google先生に 聞いてみた。そしたら、fedoraの人やdebianの人やcygwinの人まで、同じ症例に 悩まされている事が判明! なんてこったい。

でも、一つヒントを貰えたよ。.ddd/logを見ろと。DDD本社へは、証拠を送って改善 依頼してあるようだ。へー、そうなんだ。

<- "(gdb) "
#  Creating display...
#  Creating display...done.
>> "set term xlib\n"
   "set parametric\n"
   "set urange [0:1]\n"
   "set vrange [0:1]\n"
   "set trange [0:1]"
>> "set noborder\n"
   "plot \"/var/tmp//dddzPXaFy\" title \"r\""
2010.05.03 10:54:37
-> "output &r\n"
#  Display 1: r (enabled, scope main)
<< "G0 6291459\n"
   "QD\n"
   "QF\n"
     :
   "L-002\n"
   "E\n"
   "t0000-0.839442, -0.265854"
2010.05.03 10:54:37
<- "(double (*)[4096]) 0x80496a0"
2010.05.03 10:54:37
<- "(gdb) "
#  Display 1: r (enabled, scope main, address 0x80496a0)
2010.05.03 11:08:04
-> "quit\n"

これを見ると、どうやら下請けのgnuplotはちゃんと仕事をこなして、本社へ物を納品してる っぽい。やっぱり本社がサボってるんだな。

DDD News                                                -*- text -*-
********

DDD-3.3.12
==========

- Consolidated support for pydb, bashdb and remake.

- Miscellaneous bug fixes.

- DDD development is now hosted by the GNU project at

      http://savannah.gnu.org/projects/ddd

- You can download the latest code from the Subversion repository at
  savannah.gnu.org.

- The preferred location for bug reports is the Savannah bug tracker
  `http://savannah.gnu.org/bugs/?group=ddd'.

- The current maintainer of DDD is Peter Wainwright
  <peter.wainwright@ieee.org>.

これ、DDDニュースです。独自路線から、GNU傘下になったんですかね。Bug潰しは進んで いないように見受けられるぞ。

ええい、もうDDDなんてあてに出来んわ! 自分がgnuplotを操る事にしよう。でも、その 前に難題があるなあ。

gdbと交信せよ

やりたい事は、gdbを使って任意の場所で停止させ、その時の任意の配列に入っている 値をグラフ表示したいんだ。グラフ表示は、gunplotに丸投げしちゃおう。DDDみたいに、 内部で加工とかしようとするから、Bugの温床になるんだな。

すると、gbdからいかにdataをgnuplotに渡すかが、鍵になりそう。手元にASCIIから出てた gdbのマニュアル本があるけど、ちと情報不足の感がある。こういう時は、本家を訪ねる のが吉かな。

こちらが最新みたいだけど、 ちょっとずるして、 gdb manual あんちょこも見つけた。

つらつらと見てたら、emacsからgdbをよく使ってる事を思い出した。emacsとgdbのやりとり は、どうしてるのだろう? ヒントがありそうだな。

41712   2  S+     0:01.53 emacs -fg gray -bg black -cr gold -geometry 80x42 test.c
41713  p0  Ss+    0:00.06 /usr/bin/gdb --annotate=3 a.out
41714  p0  TX     0:00.01 /usr/home/sakae/ddd/a.out

これを見ると、annotateモードで実行すればよさそうな事が分かる。で、実際に やってみると、結構応答がうざい。よって、もう少し別の方法でやってみる。

MYDDD

いろいろ検討したけど、gdbとgnuplotの両方を旨く操る本社機構が必要という結論に達した。 本社は、余計なことはせず、接着剤の機能だけに徹するのが吉。

接着剤に最適なrubyをリハビリのために使ってみる。

#!/usr/local/bin/ruby
# Usage: mydd.rb a.out
# add gdb-cmd: plot array[n] @ length

def show(p)
  while ln = p.gets
    next if ln =~ /^\&/                         # dsable echo
    return if ln =~ /\(gdb\)/                   # gdb/mi prompt
    print ln                                    # result
  end
end

def rungdb(target)
  p = IO.popen("gdb -q -i mi #{target}", "w+")
  show(p)
  return true,p
end

def fin(p)
  p.print '-gdb-exit'
  return false
end

def do_cmd(cmd, p)
  p.print cmd
  show(p)
end

def graph(cmd, p)
  p.print cmd.sub(/plot/, 'output')
  g = IO.popen('gnuplot -persist','w')
  g.print 'plot "-" with lines ', "\n"
  while ln = p.gets
    next if ln =~ /^\&/
    if ln =~ /\(gdb\)/
      g.print "e\n"
      g.close
      return
    end
    if ln =~ /[-+]?(?:[0-9]+(\.[0-9]*)?|(\.[0-9]+))([eE][-+]?[0-9]+)?/ # number
      g.print $&, "\n"
    end
  end
end

### main
target = ARGV.shift
run,p = rungdb(target)
do_cmd("set print elements 0 \n", p)  # unlimited print array size
do_cmd("set print repeats  0 \n", p)  # print same elements
do_cmd("b main \n", p)                # set break on main
while(run) do
  print 'mydd> '
  cmd = gets
  if cmd =~ /plot/                    # plot cmd
    graph(cmd, p)
  elsif cmd =~ /quit/                 # quit cmd to gdb-exit
    run = fin(p)
  else
    do_cmd(cmd, p)                    # other gdb cmd
  end
end
system('pkill gnuplot_x11')           # clean up

相変わらず、class定義、何それ? のレベルで、matzさんに怒られそうだけど、いにしえの 昔からの私のスタイルなんで、お目こぼしを。

[sakae@cdr /usr/home/sakae/ddd]$ ./mydd.rb a.out
^done
^done
^done
mydd> break 13
^done
mydd> run
^done,reason="breakpoint-hit",bkptno="1",thread-id="0",frame={addr="0x08048410",func="main",args=[],file="test.c",line="7"}
mydd> cont
^done,reason="breakpoint-hit",bkptno="2",thread-id="0",frame={addr="0x08048446",func="main",args=[],file="test.c",line="13"}
mydd> plot r[0] @ 4096
mydd> plot r[1000] @ 500
mydd> quit

上記は実行例である。そそっかしい自分のために、break main した状態で、プロンプトが 出るようにしてるけど、うざかったら、コメントにしてください。

plotコマンドの @ に続く数値は、幾つ分のデータをグラフに取り込むかの指定になります。 配列の途中からグラフ表示するように指定しても、INDEX番号を表すX軸は O からの表示 になりますので、笑って許してください。(表示範囲をグラフのtitleにでも出せば、良い のでしょうが)

quitコマンドを発行すると、ターゲットプログラムがrunning中でも、有無を言わさず終了 し、グラフ表示も消されてしまいますので、あしからず。

Arch Linuxでは難有り

FreeBSDのgdbは、

[sakae@cdr ~]$ gdb -v
GNU gdb 6.1.1 [FreeBSD]

こんな具合。ArchLinuxのそれは、gdb7.Xだった。多分そのせいだろうと思うけど、plotの 表示がおかしくなった。gdbとのやりとりが、現在進行形の機構を使ったからかな?

.gdbinitあたりに

define dumparry 
  set $COUNT = $arg2
  set $INDEX = $arg1
  while $COUNT
     output $arg0[$INDEX]
     echo \n
     set $INDEX = $INDEX + 1
     set $COUNT = $COUNT - 1
  end
end

とでもしておいて、output r[10] @ 20 の所を dumparry r 10 20 とでもすれば良いのかな? 考えるよりも手を動かしてみよう。gbdとセッションして、相手の手のうちを把握。 以下は、それを元に調整してみたもの。gbd 7.1から進化したら、また手のうちが 変わるのだろうか?

#!/usr/bin/ruby
# Usage: mydd.rb a.out
# add gdb-cmd: plot array[n] @ length

def show(p)
  while ln = p.gets
    next if ln =~ /^\&/                 # dsable echo
    break if ln =~ /\*running/
    return if ln =~ /\(gdb\)/           # gdb/mi prompt
    print ln
  end
  p.gets                                # dummy read for (gdb)
  while ln = p.gets
    return if ln =~ /\(gdb\)/           # gdb/mi prompt
    print ln
  end
end

def rungdb(target)
  p = IO.popen("gdb -q -i mi #{target}", "w+")
  show(p)
  return true,p
end

def do_cmd(cmd, p)
  p.print cmd
  show(p)
end

def graph(cmd, p)
  system('pkill gnuplot')           # clean up
  p.print cmd
  g = IO.popen('gnuplot -persist','w')
  g.print 'plot "-" with lines', "\n"
  while ln = p.gets
    next if ln =~ /^\&/
    if ln =~ /[-+]?(?:[0-9]+(\.[0-9]*)?|(\.[0-9]+))([eE][-+]?[0-9]+)?/ # number
      g.print $&, "\n"
    end
    if ln =~ /\(gdb\)/
      g.print "e\n"
      g.close
      return
    end
  end
end

### main
target = ARGV.shift
run,p = rungdb(target)
do_cmd("set print elements 0 \n", p)  # unlimited print array size
do_cmd("set print repeats  0 \n", p)  # print same elements
while(run) do
  print 'mydd> '
  cmd = gets
  if cmd =~ /plot/                    # plot cmd
    graph(cmd.sub(/plot/, 'output'), p)
  elsif cmd =~ /quit/                 # quit cmd to gdb-exit
    p.print '-gdb-exit'
    run = false
  else
    do_cmd(cmd, p)                    # other gdb cmd
  end
end
system('pkill gnuplot')               # clean up