ローグウェーブソフトウェアのブログ

開発をシンプルに 安全で高品質のコードを 素早くお客様のもとへ

TotalView Python C++混合デバッグ - ホワイトペーパー紹介

こんにちは、セールスエンジニアの柄澤(からさわ)です。先日ローグウェーブのウェブサイトで公開されたホワイトペーパー: Mixed-language debugging with Python and C/C++に基づいて、簡単な相互運用コードを作成し、一般的で困難なデバッグ手法と、先日の新しいTotalViewリリースで発表された革新的なデバッグ手段をご紹介します。

PYPLのようなサイトを見てもわかるように(2017年6月時点でJavaに次いで2位の16.1%、1年で3.8%の伸び)、プログラミング言語Pythonの人気は高まっています。Pythonスクリプト言語として使いやすいだけでなく、C/C++のコードを使って簡単にextensionを作成して機能を拡張することにより、C/C++で書かれた既存の資産を活用したり、処理の高速化や省メモリ化を実現することができます。このような相互運用性(interoperability)の高さもPythonインストーラから数値計算、データサイエンスから機械学習(ML/AI)まで幅広い分野で人気を誇っている理由の1つでしょう。

準備: C言語によるPython拡張の作成

一般的にC/C++Python拡張を作成する場合はPythonライブラリのctypesを使ったり、逆にBoost.PythonのようにC++側から手を差し伸べるライブラリもあります。今回は様々な言語を結びつけるSWIGを利用してPythonC++の混合プラグラミングの一端を覗いて見ましょう。使用するコードはSWIGのサイトにある階乗計算のサンプルを利用し、環境はUbuntu Linuxを想定しています。

まずはヘッダファイル example.h

int fact(int n);

再帰計算を実装する example.c

#include "example.h"
int fact(int n) {
  if (n < 0){
    return 0;
  }
  if (n == 0) {
    return 1;
  } else {
    return n * fact(n-1);
  }
}

PythonからCを呼ぶ時の言語間ラッパー example.i。必要に応じて細かい設定が可能ですが、今回はシンプルにヘッダファイル名と公開する関数のみ、

%module example
%{
#define SWIG_FILE_WITH_INIT
#include "example.h"
%}
int fact(int n);

次にこのexample.iからSWIGモジュールを作成します。以下はC言語の場合で、C++の場合は-C++オプションを追加してください。

$ swig -python example.i 

このコマンドによりSWIGは2つのglueコード、examle_wrap.cとexample.pyを生成します。

次に、デバッグバージョンのPythonを入手します。 ubuntuでは sudo apt-get install python-dbg python-devにより簡単にインストールできます。このPythonは /usr/include/python2.7_dにインストールされます。

では、Cモジュールをコンパイルして見ましょう。Python拡張をコンパイルする方法は、distutilsを使うなどいくつか方法がありますが、ここでは簡単のために手動でコンパイル、リンクします。

$ gcc -g -fPIC -c example.c example_wrap.c -I/usr/include/python2.7_d
$ gcc -shared example.o example_wrap.o -o _example.so 

ここで、出力される共有ライブラリの名前はPython拡張と同じ名前にし、先頭にアンダースコアをつけます。また、今回のようにデバッグバージョンのPythonヘッダを使ってコンパイルされたモジュールは、多くのデバッグ情報を含むため、通常版のPythonでは実行できないことにご注意ください。

次に、この拡張モジュールを呼び出すためのPythonコードを記述します。test.pyとしましょう。

def getFact():
  import example
  a = 3
  b = 10
  c = a + b
  return example.fact(a)
if __name__ == '__main__':
  result = getFact()
  print result

これを実行すると

$ python-dbg test.py
6

のように無事3の階乗がCモジュールで計算され、Pythonに結果が返ってきました。

課題: PythonC/C++アプリケーションの同時デバッグ

以上、簡単な拡張を作成することにより、SWIGの雰囲気を感じ取っていただけたと思いますが、実用的で複雑な拡張を作成する際には、自分で作成したモジュールが正しく動いているのかどうか確認したり、問題が発生したらその原因を突き止めるためにデバッグや動的解析の必要性が生じます。どのようなデバッグ手段が考えられるでしょうか?

Eclipseの場合

ホワイトペーパーではまずEclipseとPyDevを使った方法を紹介します。これは一般的に用いられている方法ですが、記事中でも紹介されている通りとても面倒なセットアップが必要です。詳細はホワイトペーパーを参照していただければと思いますが、C用とPython用に2つのプロジェクトを用意し、「C/C++ Attach to Application」構成を作成し、Pythonプログラムにブレークポイントを置いてデバッグ実行し、拡張コード呼び出しに近づいたら作成しておいた構成をPythonプロセスにアタッチすることでデバッグが開始されます。ランタイムをpython2.7-dbg等(Ubuntuの場合)に指定することをお忘れなく。

その結果得られるデバッグ体験はとても快適なものとは言えません。2つのデバッグプロジェクトを同時実行するためにパネルが乱立し、コールスタックはPythonとC部分は統合されておらず、しかも多段のglue呼び出しが含まれます。変数ウィンドウも言語ごとに分かれているため、同時に値をチェックするのは簡単ではありません。複雑なアルゴリズムを使ったPython拡張を作成したい場合、デバッグが困難であることは大きな障害となります。

TotalViewの場合

以上のような要請を踏まえ、TotalView 2017.1に混合デバッグ機能が導入されることになりました。

blog.roguewave.jp

TotalViewはLinuxUnixMacなどのプラットフォーム上で、CAEや金融計算からHPCまで幅広い分野でC/C++/Fortranの並列プログラムを実行時に解析するデファクトスタンダードのデバッガです。大規模並列アプリケーションの様々な粒度でのステップ実行や、メモリデバッグ、リバースデバッグ、動的パッチ機能などを提供し、最先端の研究やミッションクリティカルなアプリケーション開発に欠かせない存在です。このTotalViewの能力に、Pythonデバッグの機能が統合されることになりました。

実際にデバッグセッションのセットアップおよび実行例を見てみましょう。

先の階乗計算のプログラムを用意しておいてください。TotalViewの新UIをコマンドラインから以下のように立ち上げます。GUIの起動画面から設定することもできます。

$ totalview -newui --args /usr/bin/python2.7-dbg test.py

起動時にはSource not availableと表示されていると思います。次にCコードにブレークポイントを設定します。メニューの Action Points > At Locationウィンドウからソースコードのファイル名と行番号を example.c#7 のように指定することによりすぐにブレークポイントを設定できます。ダイアログで「Subroutine “example.c#7” not found. Make it a pending breakpoint for a future shared library load?」と訊かれます。Yesを押してください。これらは、python拡張の共有ライブラリ_example.soがまだ読み込まれていないことに起因します。

なお、glueコードを隠してスタックトレースをすっきりと表示する機能はTotalView 2017.1ではデフォルトでオフになっているため、以下のコマンドをTotalView CLIウィンドウで実行する必要があります。

dstacktransform enable

任意のタイミングで実行できます。このオプションは今後のリリースで、デフォルトでオンになる予定です。

あとは通常通りTotalViewでデバッグを開始するだけです。アプリケーションは当然のように先ほど置いたブレークポイントで停止し、Cで書かれたコードがTotalViewに表示されます。

f:id:RWSJapan:20170619105000p:plain

右側のパネルをご覧ください。TotalViewを使った場合、C/C++Pythonのコールスタックが完全に統合され、しかも自動生成された煩わしいglueコードは省略されています。つまり開発者が意識すべき部分だけが表示されているのです。この機能を私たちはSTF(Stack Transformation Facility)と読んでいます。これはTTFやC++View機能などにもみられるような開発者目線の機能の好例と言えるでしょう。

blog.roguewave.jp

blog.klocwork.com

VARパネルで両方の言語の変数内容を同時に表示することもできます。VARパネルへの登録は通常通り変数をパネルにドラッグするだけです。

そして、TotalViewの先進的なデバッグ機能、ReplayEngineを使ったリバースデバッグを使い、実行記録を保存したりブックマークして好きなタイミングで見たい実行箇所へとジャンプすることができます。これによりさらにデバッグ開始までの手間を削減することができます。

ほぼ同一の内容ですが、機能の説明がTotalViewの新UI (CodeDynamics)のリファレンスに記載されています。

結論

以上見てきたように、このTotalViewのC/C++Python混合デバッグ機能は余計な手間をかけずに通常のブレークポイントなどの自然な手段を用いてPython拡張の開発を加速させます。ますます広まっているAI/ディープラーニングの領域で用いられるTensorFlowやChainer、NumPy/SciPyによる高度な数値/統計計算を駆使するHPCや金融の領域などで、既存のフレームワークをさらに拡張して自分たちに適した高度で複雑なアプリケーションを開発したい方は、一度TotalViewのこの新機能を試してみることをおすすめします。

サンノゼで行われたNVIDA GTCでの発表スライドを再度貼っておきます。

www.slideshare.net

Windows環境ではVisual Studioも有力な選択肢ですね。

Visual Studio での Python の混合モードのデバッグ | Microsoft Docs

セールスエンジニア 柄澤(からさわ)