3

ここで誰かが私を正しい方向に向けることができるかどうか知りたいです。私はコンピューター システムのプログラミング (基本) について学んでおり、さまざまなレベルでコードをトレースして、それぞれがどのように相互作用するかを確認しようとしています。例として、C またはC++ などでfgets()関数を呼び出します。getline()どちらもシステムを正しく呼び出しますか? 呼び出されたコードを見る簡単な方法はありますか?

私はUnix(Ubuntu)に取り組んでいます。これは Windows と Apple 独自のものですか? この種の良いリソースはありますか? いつものように、みんなありがとう!

4

4 に答える 4

2

Windows では、いくつかのことで洞察を得ることができます。まず、調査したいバイナリに対応するシンボル ファイルと呼ばれるものが必要です。シンボル ファイルは、テキスト名を、プログラム内に存在するグローバル/スタック/ヒープ変数に関連付けます。したがって、メモリ内のアドレスを関数 fgets にマップし、特定のプログラムで fgets を確認するには、Microsoft の C std ライブラリの実装のバージョンのシンボルが必要です。Lucky for you MS はシンボルを自由に利用できるようにします

次に、fgets よりも深く掘り下げたコールスタックをキャプチャする必要があります。これを行う最も明白な方法は、Microsoft の開発者であり、ディープ MS dll にクラッシュを導入し、デバッガーとシンボルを使用してクラッシュ ダンプを分析することですが、残念ながらそれはできません。できることは、サンプリング プロファイラーと呼ばれるものを使用することです。たとえば、Microsoft から無料で入手できます。サンプリング プロファイラーは、プログラムのコールスタックのスナップショットを定期的に取得して、コードをプロファイリングします。Microsoft のシンボル ファイルを使用すると、そのコールスタックを意味のあるものに消化できます。

これら 2 つの情報があれば、プログラムを作成して fgets が何を呼び出すかについての洞察を得ることは難しくありません。次に、サンプリング プロファイラーを Microsoft のシンボルと共に使用して、プログラム中に何が起こっているかを把握できます。

これらの線に沿って、これを試すために次のプログラムを作成しました。

int FgetSTest()
{
    FILE* fp;
    fp = fopen("C:/test.txt", "w");
    char data[100];
    int sum = 0;
    for (int i = 0; i < 100; ++i)
    {
        fgets(data, 100, fp);
        sum += data[0];
    }
    fclose(fp);
    return sum;

}



int _tmain(int argc, _TCHAR* argv[])
{
    int sum  = 0;
    for (int i = 0; i < 100; ++i)
    {
        sum += FgetSTest();
    }
    std::cout << sum;
    return 0;
}

これをプログラムにコンパイルしたと仮定すると (私は perfPlay.exe という名前のプログラムにコンパイルしました)、次のように exe で MS のサンプリング プロファイラーを実行できます。

C:\path\to\exe>vsperfcmd /start:sample /output:perfPlay.vsp
Microsoft (R) VSPerf Command Version 9.0.30729 x86
Copyright (C) Microsoft Corp. All rights reserved.



C:\path\to\exe\>vsperfcmd /launch:perfPlay.exe
Microsoft (R) VSPerf Command Version 9.0.30729 x86
Copyright (C) Microsoft Corp. All rights reserved.


Successfully launched process ID:3700 perfPlay.exe
sum is:40000
C:\path\to\exe>vsperfcmd /shutdown
Microsoft (R) VSPerf Command Version 9.0.30729 x86
Copyright (C) Microsoft Corp. All rights reserved.


Shutting down the Profile Monitor
------------------------------------------------------------

プロファイラーの出力を取得します。コマンドが Microsoft のシンボル サーバーを指すように「symbolpath」スイッチがあることに注意してください。

 C:\path\to\exe>vsperfreport perfplay.vsp /summary:all /symbolpath:srv*c:\symbols*htt

p://msdl.microsoft.com/download/symbols

呼び出し元と呼び出し先のレポートの csv を直接調べるか、私が取り組んできたような優れたビューアーを見つけることができます。fgets がほとんどの時間を費やしている場所を把握できます。

fgets がどのように時間を費やすかを視覚的に表現

悲しいことに、ひどく洞察力がありません。残念ながら、このアプローチで遭遇する問題の 1 つは、リリース モードで fgets が呼び出す関数の多くがインライン化される可能性が非常に高いということです。つまり、関数は最終的なプログラムから関数としてほとんど削除され、その内容は直接 "使用する場所に貼り付けます。

インライン化の可能性が少ないため、デバッグ モードで上記を繰り返して、何が得られるかを確認できます。

于 2012-06-13T22:28:07.053 に答える
2

少なくとも UNIX の世界では、答えはかなり簡単です。「ソースを使用してください、ルーク」。

あなたの例では、たとえば fgetc() のソースを調べます。これは C 標準ライブラリにあり、ソースを見つける最も簡単な方法は、「C ライブラリ fgets() ソース」のようなものをグーグルで検索することです。

そのソースを取得すると、バッファなどを処理する一連のコードと、おそらくread(2)へのシステム コールが表示されます。そこにある "2" は、マニュアルの第 2 章に記載されていることを示しています (たとえば、 で見つけることができます)。man 2 read

システム コールはカーネルに実装されているため、カーネル ソースを読み込む必要があります。そこから進みます。

ソースを無作為に読む必要なしにこれらすべてを見つける必要があるのは (多くの人がそのように学んでいますが、あまり効率的ではありません)、Kerrisk のThe Linux Programmingのような Linux に関する本を手に入れることです。インターフェイス、これらのことのいくつかを単なるソースよりもやや高いレベルで説明します.

于 2012-06-13T22:11:21.790 に答える
2

内に何かfgetsがありますlibc。つまり、ほとんどの C バイナリにリンクされたユーザーランド ライブラリです。glibc現在最も一般的な実装である を確認してください。

最終的にlibcは、カーネルへのシステム コールの作成を開始します。ソースはkernel.orgで入手できます。カーネルのデバッグについては、KGDBを確認してください。カーネルのデバッグを行う最も簡単な方法は、ヌル モデル ケーブルを介して接続された 2 台目のマシンを使用することです。

于 2012-06-13T22:12:34.073 に答える
1

まず最初に。このタスクには優れたツールが必要です。etagsソースをナビゲートするときに、、、cscopeおよびgid(GNU からidutils) 不可欠なツールを見つけました。これらの 1 つまたは複数をお気に入りのエディターまたは IDE に統合する方法を考えてください。これらの機能を利用するには、エディターまたは IDE を切り替えてください。貧弱なツールの言い訳はできません。1 つのアドバイスを探しているなら、私は が大好きですvim。非常に多くの人がemacsを支持しており、Eclipse を愛用している人もいます。

ソースがローカルに必要です。lxrは素晴らしいツールですが、Web リクエストの繰り返しに伴う遅延は、深刻な作業を行うには疲れます。Debian 派生システムでは、これは非常に簡単です。ソースを保存する場所にディレクトリを変更し、実行してソースapt-get source eglibcをダウンロードしglibcます。http://www.kernel.orgから tarball を介してカーネル ソースを取得するか、マスターリポジトリのクローンgitを作成することをお勧めします (変更ログを読みたい場合や簡単に更新を取得したい場合は、より良い選択ですが、6 月の時点で 2.7 ギガバイトまで拡張されます)。 2012年なので、明らかに万人向けではありません)。

C ライブラリのタグ ファイルを作成したら、次を実行するだけで、ルーチンのソースが直接vim -t fgets開きます。(期待しているよりもはるかに読みにくいです。) 最終的にシステム コールにたどり着くまで、これらに従ってください。(時間がかかる場合があります。)libio/bits/stdio2.hfgets()read()

ここで、カーネル ソースに切り替えます。これを調べてくださいfs/read_write.c

SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)

カーネルがマクロを使用してシステム コールを定義する方法の欠点の 1 つは、関数の検索が複雑になることです。vim -tこれを直接見つけることはできません。システム コールを探すときの最も簡単な方法は、実行することgid -s SYSCALL_DEFINE | grep readです。(より良いツールを見つけたら、私に知らせてください。) システム コールのエントリ ポイントを見つけたら、カーネル ソースの残りの部分を読むのがはるかに簡単になります。(一般的に、glibcソースよりも読みやすいと思いますが、ブロックレベルの呼び出しから 5 つまたは 6 つの関数呼び出しが行われる時代はとうのbread()昔に過ぎ去っています。)

于 2012-06-13T22:57:11.687 に答える