15

これは私のコードの構造の縮小例です:

void increment(int j);

int main()
{
  int i = 0;

  while(1) {
    i = increment(i);
  }

  return 0;
}

int increment(int j)
{
  return j + 1;
}

対応するGDBスクリプトは次のとおりです。

b increment
command 1
finish
print i
continue
end

問題は、finishコマンドがその後に続くコマンド(つまりprint i、とcontinue)が呼び出されないようにすることです。

i呼び出しの直後に印刷するようにGDBに指示する方法はありincrementますか?

4

5 に答える 5

15

どうやらこのバグを回避するには、すべてのコマンドを1回のPython呼び出しでラップします。

(gdb) break doSomething
Breakpoint 1 at 0x400478: file iter.c, line 5.
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>python gdb.execute("print i"); gdb.execute("finish"); gdb.execute("print i");
>end

Breakpoint 1, doSomething () at iter.c:5
5     while (i < 5)
$1 = 0
main (argc=1, argv=0x7fffffffe178) at iter.c:13
13    return 0;
$2 = 5

編集:Pythonを必要としない2番目の回避策は、新しいgdbコマンドを定義し、それをコマンドで実行しているようです。

define foo
print *i
set $addrOfI = i
finish
print *$addrOfI
end

break doSomething
commands
foo
end
于 2012-05-10T07:11:51.197 に答える
4

問題は、finishが、その後の最初のブレークポイントに設定されたコマンドの中止を停止しているように見えることです。

これは予想される動作です。(デバッグ中の)下位プロセスを再開するコマンドはfinish、既定のコマンドシーケンスの実行も停止します。

アップデート:

このGDBバグレポートも参照してください。

増分呼び出しの直後にiを出力するようにGDBに指示する方法はありますか?

はい:

  1. incrementコマンドを使用してルーチンを分解しdisasます。retその最後にある命令を見つけてください(1つだけになります)。
  2. break *0xNNNNN構文を使用して、その命令にブレークポイントを設定します。
  3. そのブレークポイントにコマンドを添付します。

    command N
     print $rax    # or $eax if you are on 32-bit x86 platform
     continue
    end
    

出来上がり:increment()印刷から返される値を取得する必要があります(返される直前)。

于 2012-05-08T14:58:59.987 に答える
3

@Mattの回答の代わりに、GDB 7.4を使用している場合は、FinishBreakpointsを次のように使用できます(テストされていません。ここでコメントが受け入れられるかどうかはわかりません)。

(gdb) python #first defined the class
class MyFinishBreakpoint (gdb.FinishBreakpoint):
    def stop (self):
        print "%s" % gdb.parse_and_eval("i")
        return False # don't want to stop
end
(gdb) break doSomething
(gdb) commands
# then set the FinishBreakpoint silently
silent
py MyFinishBreakpoint()
continue

(およびドキュメントへのリンク)

于 2012-05-11T07:34:29.430 に答える
1

これを実際にコンパイルしようとしましたか?関数increment()は宣言されvoidていますが、である必要がありますint。それを変更した後、それは私にとってうまくいきました:

% gdb test
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
[...]
Reading symbols from test...done.
(gdb) b increment 
Breakpoint 1 at 0x4004bb: file test.c, line 5.
(gdb) r
Starting program: test 

Breakpoint 1, increment (j=0) at test.c:5
5               return j+1;
(gdb) fin
Run till exit from #0  increment (j=0) at test.c:5
0x00000000004004dc in main () at test.c:11
11                      i = increment(i);
Value returned is $1 = 1
(gdb) n
12              }
(gdb) p i
$2 = 1
(gdb)
于 2012-05-08T15:50:14.357 に答える
1

GDBブレークポイントコマンドリストは、最初のステッピング/続行コマンドの後のコマンドを無視するという制限があります(2017年3月現在、GDB 7.12)。これはGDBマニュアルに記載されており、現在の実装では2つのコマンドリストを同時に実行できません(GDB#10852を参照-コマンドシーケンスが予期せず中断されました)。

この制限は、コマンドリストに直接存在するstepping/continueコマンドでのみ適用されます。したがって、これを回避することはできますが、制限は引き続き適用されます。たとえば、GDBマニュアルはPython APIセクションで警告します。「下位の実行状態(つまり、ステップ、次など)を変更しないでください」

したがって、関数の開始時と終了時にGDBコマンドを実行する必要が生じた場合、信頼できる解決策は、複数のブレークポイントを使用してコマンドリストを分割することです。つまり、調査中の関数の戻り命令ごとに追加のブレークポイントを設定する必要があります。

これは、次のように実行できます。

(gdb) b my_function
(gdb) commands
silent
printf "my_function: %d -> ", j
end
(gdb) set pagination off
(gdb) set logging file gdb.log
(gdb) set logging overwrite on
(gdb) set logging on
(gdb) disas my_function
(gdb) set logging off
(gdb) shell grep ret gdb.log
0x00007ffff76ad095 <+245>:  retq
(gdb) b *0x00007ffff76ad095
(gdb) commands
silent
printf "%lu\n", $rax
end

どのレジスタに戻り値が含まれるかは、呼び出し規約によって異なり、アーキテクチャによって異なります。x86-64では、にあり$raxます。他の選択肢は$eax、x86-32、$o0SPARC、$r0ARMなどです。

追加のブレークポイントの作成は、スクリプトサポートを使用してGDBで自動化できます。

Python

最近のGDBバージョンには、この自動化に最適なPythonAPIが付属しています。ディストリビューションによって提供されるGDBパッケージは、通常、デフォルトでPythonサポートを有効にします。

最初の例として、特定の関数の各ret命令にブレークポイントを自動的に設定します。

(gdb) py fn='myfunc'; list(map(lambda l: gdb.execute('b *{}'.format(l[0])), \
    filter(lambda l : l[2].startswith('ret'), map(lambda s : s.split(), \
        gdb.execute('disas '+fn, to_string=True).splitlines()))))

(GDBがPython3サポート(Fedora 25など)でコンパイルされていると仮定します)

戻り値(つまり、レジスタの値$rax)を出力してから続行するブレークポイントの作成を自動化するには、gdb.Breakpointクラスをサブクラス化する必要があります。

py
class RBP(gdb.Breakpoint):
  def stop(self):
    print(gdb.parse_and_eval('$rax'))
    return False

end

次に、次のようにブレークポイントを作成できます。

py RBP('*0x000055555555894e')

新しいカスタムコマンドを作成するための両方の手順を組み合わせる:

py
class Pret_Cmd(gdb.Command):
  '''print return value via breakpoint command

  pret FUNCTION
  '''
  def __init__(self):
    super().__init__('pret', gdb.COMMAND_BREAKPOINTS)

  def install(self, fn):
    for l in filter(lambda l : l[2].startswith('ret'),
        map(lambda s : s.split(),
            gdb.execute('disas '+fn, to_string=True).splitlines())):
      RBP('*{}'.format(l[0]))

  def invoke(self, arg, from_tty):
    self.install(arg)

Pret_Cmd()
end

この新しいコマンドの使用例:

(gdb) help breakpoints
(gdb) help pret
(gdb) pret myfunc

Guile

Pythonが気に入らない場合や、Pythonサポートが無効になっているGDBがあるが、Guileサポートが有効になっている場合は、Guileを介してブレークポイントを自動的に設定することもできます。

Guileのカスタムコマンド定義:

(gdb) gu (use-modules (gdb))
(gdb) gu
(register-command!
  (make-command "pret" #:command-class COMMAND_BREAKPOINTS #:doc
    "print return value via breakpoint command\n\npret FUNCTION"
    #:invoke
    (lambda (fn)
      (map (lambda (x)
             (let ((bp (make-breakpoint (string-append "*" x))))
               (register-breakpoint! bp)
               (set-breakpoint-stop!
                 bp
                 (lambda (x)
                   (display (parse-and-eval "$rax"))
                   (newline)
                   #f))
               bp))
           (map (lambda (x) (list-ref x 0))
                (filter
                  (lambda (x)
                    (and (not (null? x))
                         (string-prefix? "ret" (list-ref x 2))))
                  (map (lambda (x) (string-tokenize x))
                       (string-split
                         (execute
                           (string-append "disas " fn)
                           #:to-string
                           #t)
                         #\newline))))))))

end
于 2017-03-05T10:10:51.583 に答える