linuxBean14.04(162)LibreOfficeのC++の拡張機能の例をCUIのデバッガにかける

ラベル: , ,
LibreOffice 5.3 SDK - Examplesのcomplextoolbarcontrolsの例をgdbでデバッガにかけようと思ったのですが、IDEでのやり方がよくわからずまずgdbのCUIでとっかかりが掴めました。

前の関連記事:linuxBean14.04(161)brush-Makefileのバグを修正する


complextoolbarcontrolsのインストール


complextoolbarcontrolsのC++の例はLibreOfficeのSDKに含まれています。

コンパイルはlinuxBean14.04(132)LibreOfiice5.2のSDKの例をコンパイルするでやりました。

個別にコンパイルするときはデバッグ情報を付加するためにmake DEBUG=yesでやります。

コンパイル後~/libreoffice5.2_sdk/LINUXexample.out/binにcomplextoolbarcontrols.oxtができているのでそれをLibreOfficeの拡張機能マネージャーで登録します。


Writerを起動するとこんなツールバーが表示されます。

表示→ツールバー→Complex Toolbar Controlsでツールバーを消せます。

このツールバーが何をしているのかは、この時点では左端の画像ボタンをクリックするとブラウザが起動してLibreOfficeのホームページが表示されるということしかわかりませんでした。

で、これをLibreOffice5(28)MRI - UNO Object Inspection Tool:その4のPythonの拡張機能と同様にしてデバッガにかけようとしましたがだいぶ四苦八苦しましたがとりあえずgdbのCUIでやる方法がわかりました。

どこにブレークポイントを張るのか決める


まずどこにブレークポイントをかけるのか/opt/libreoffice5.2/sdk/examples/cpp/complextoolbarcontrolsにあるソースをみて決めておきます。
    if ( aURL.Protocol == "vnd.demo.complextoolbarcontrols.demoaddon:" )
    {
        if ( aURL.Path == "ImageButtonCmd" )
        {
   
            // open the LibreOffice web page
            ::rtl::OUString sURL("http://www.libreoffice.org");
            Reference< XSystemShellExecute > xSystemShellExecute(
                SystemShellExecute::create(mxContext) );
MyProtocolHandler.cxxの220行目にツールバーの画像ボタンをクリックしたときに起動される部分を見つけました。

で、どうやってブレークポイントを設定するかです。
(gdb)  break <クラス名>::<関数名>
(gdb)  break <ファイル名>:<行番号>
(gdb)  break <ファイル名>:<関数名>
GDBの使い方 - PukiWiki
この解説をみつけてようやくわかりました。

まず<クラス名>::<関数名>で設定してみることにしました。

C++ではクラス定義をヘッダファイルに書くことが一般的だそうで、この例でもMyProtocolHandler.hに書いてありました。

MyProtocolHandler.cxxの220行目に該当するクラスはMyProtocolHandler.hの117行目にあってクラス名はBaseDispatch、関数名はdispatchとわかりました。
void SAL_CALL BaseDispatch::dispatch( const URL& aURL, const Sequence < PropertyValue >& lArgs ) throw (RuntimeException)
まあヘッダファイルを見なくてもcxxファイルだけみても207行目に<クラス名>::<関数名>が書いてありました。

SAL_CALLというのが何かわからなかったのですが、これはWhat is SAL_CALL in c++? - Stack Overflowに解説がありました。

文字列置換のマクロが定義してあって、マイクロソフトのコンパイラを使うときにだけ__cdeclという文字列が入るようです。

Linuxではgccとg++を使うので関係なさそうです。

ちなみにgccとg++の違いについて調べてみるとg++はgccに-lstdc++オプションを付けたものだそうです。([roast] 2012/5/19 -> gccとg++って、厳密には違うらしいね。 | myun2's blog

complextoolbarcontrolsのMakefileの出力ログを見るとまずgccでcxxファイルからoファイルを作って、最後にg++で複数のoファイルをまとめてsoファイルにしています。

基本を再チェック~gcc~@謎の処理系 SunOS 4.1.4を読むとgccでC++をコンパイルできる例もあるようです。

gdbで拡張機能のC++をブレークする


/opt/libreoffice5.2/programでTerminalを起動します。

gdb

まずこれでgdbを起動しました。
pq@pq-VirtualBox:/opt/libreoffice5.2/program$ gdb
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
プロンプトが(gdb)に変化します。

file soffice.bin

LibreOfficeの本体をgdbに読み込みます
(gdb) file soffice.bin
Reading symbols from soffice.bin...(no debugging symbols found)...done.
LibreOffice本体はバイナリをインストールしたものなのでデバッグ情報が含まれていませんが、デバッガでみたいのは拡張機能の部分だけなので問題ありません。

break BaseDispatch::dispatch

先ほど決めたクラス名と関数名を使ってブレークポイントを設定しました。
(gdb) break BaseDispatch::dispatch
Function "BaseDispatch::dispatch" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (BaseDispatch::dispatch) pending.
まだgdbが拡張機能を読み込んでいない状態なのでBaseDispatch::dispatchがないと言われます。

まあそういわれると予測していたのでそれ以外の方法を調べていて四苦八苦していたわけですが、やってみるとgdbでは定義が見つからないところにもyと回答するとブレークポイントが貼れました。

run

これでfileコマンドで読み込んでいたsoffice.binをgdbから起動しました。
(gdb) run
Starting program: /opt/libreoffice5.2/program/soffice.bin 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
[New Thread 0xb1c7fb40 (LWP 6898)]
[New Thread 0xb05d2b40 (LWP 6968)]
[New Thread 0xafbffb40 (LWP 6969)]
[New Thread 0xab38bb40 (LWP 6970)]
[New Thread 0xaa4beb40 (LWP 6971)]
[New Thread 0xa9cbdb40 (LWP 6972)]
[Thread 0xb05d2b40 (LWP 6968) exited]
[New Thread 0xb05d2b40 (LWP 6973)]
[New Thread 0xa91cfb40 (LWP 6976)]
[Thread 0xa91cfb40 (LWP 6976) exited]
[New Thread 0xa91cfb40 (LWP 6977)]
[New Thread 0xa867bb40 (LWP 6978)]
[Thread 0xa91cfb40 (LWP 6977) exited]
[New Thread 0xa91cfb40 (LWP 6979)]
[Thread 0xa91cfb40 (LWP 6979) exited]
[New Thread 0xa91cfb40 (LWP 6980)]
[Thread 0xa91cfb40 (LWP 6980) exited]
LibreOfficeのスタート画面がでてくるのでそこから「Writer文書ドキュメント」ボタンをクリックしてWriterを起動しました。

それで表示されたComplex Toolbar Controlsツールバーの画像ボタンをクリックするとWriterが止まります。

gdbを起動しているTerminalをみると先ほど設定したブレークポイントでブレークされていました。
Breakpoint 1, BaseDispatch::dispatch (this=0xa79883a0, aURL=..., lArgs=...)
    at MyProtocolHandler.cxx:216
216     Reference< XInterface > xSelfHold(static_cast< XDispatch* >(this), UNO_QUERY);
list

listあるいはlでブレークされている部分のソースが表示されます。
(gdb) l
211        forget this instance during de-/activation of frames (focus!).
212 
213         E.g. An open db beamer in combination with the My-Dialog
214         can force such strange situation :-(
215      */
216     Reference< XInterface > xSelfHold(static_cast< XDispatch* >(this), UNO_QUERY);
217 
218     if ( aURL.Protocol == "vnd.demo.complextoolbarcontrols.demoaddon:" )
219     {
220         if ( aURL.Path == "ImageButtonCmd" )
あとはgdbのコマンドを使ってデバッグができます。

どうも変数の内容が見れない、、、というかUNOの型をデバッガが解釈できていないようです。

gdb自体の使い方を習熟していないので、まだよくわらないですね。

さらに調べてみるとDebugging - Apache OpenOffice WikiGDB: How to dump Unicode Strings (i.e. provide custom data display)にOUString型などを変換する方法が載っていますが、LibreOffice本体のソースが必要なようで、うまくコンパイルできませんでした。

(2017.4.23追記linuxBean14.04(163)OUStringの文字列をgdbで見る方法で変数の内容を見る方法がわかりました。)

q

gdbはqで終了できます。

ここに至るまでやってみたこと


できてしまえばとくに特殊なことをするわけでもなく標準的なgdbの使い方でできてしまう簡単なことだったわけですが、そうできるとも知らずこれまでLibreOffice5(28)MRI - UNO Object Inspection Tool:その4のPythonの拡張機能と同様にしてデバッガにかけようとして四苦八苦した経過を書いておきます。

ps x | grep soffice

これでLibreOfficeのプロセスIDを調べてgdbをアタッチします。

sudo gdb -p プロセスID

最初はこれでなんとかしようとしたのですが、LibreOfficeをGUIから操作できなくなるのでなんともできませんでした。

DebugBreakとプロセスアタッチでC++アプリケーションをデバッグする - 大人になったら肺呼吸

これを読んでWindowsではDebugBreakを使えばPythonのときと同様にブレークポイントをソースに設定してそこにきたらそこからデバッガが起動できることがわかりました。

私はlinuxBeanで開発しているのでこの方法は使えません。

Cのプログラムの中でブレークポイントを設定する - bkブログ

x86限定なら__asm__("int3");を挿入するだけでブレークポイントをソースに埋め込めることがわかりました。

それでもう少し汎用的な方法はないか探したところGitHub - scottt/debugbreak: break into the debugger programmaticallyを見つけました。

これをローカルにクローンしてtestに含まれているgdbファイルのコマンドをやってみて、わざわざソースにブレークポイントを埋め込まなくてもgdbからLibreOfficeの拡張機能のC++のソースにブレークポイントを設定できることに気が付いたのでした。

ちなみdebugbreakなどを挿入したコードをgdb以外から実行するとLibreOffice本体が異常終了しますので本番では挿入したコードを消してコンパイルし直さないといけません。

C++はPythonに比べるとなかなか最初の学習のハードルが高いですね。



ちなみに私は最初にとりあえずこの「C++の絵本」という本を買って学習しました。

いまは第二版がでています。

この絵本シリーズは「Cの絵本」「Javaの絵本」「SQLの絵本」も購入して参考にしています。

今はネットでいろいろ調べられるので手元に置いておく本はこのような絵本が便利ですね。



Cについては20年以上前にこの「独習C」という本を購入して格闘した思い出があります。

当時はWindows95が出た頃でCのコンパイラは趣味で購入するには高価だったため、大学で使えるコンピュータでこの「独習C」を読みながら独習しようとしたのですが、全くコンパイルできずに本を読んだだけで終わりました。

いま振り返れば大学のコンピュータはNeXTcubeだったためCを動かすには特殊な設定が必要だったのだと思います。

何かヒントが得られると思って受けた「情報科学」の講義はPCは使わずPascalでハノイの塔のプログラムを紙と鉛筆で実行していくというアルゴリズムの学習だけでした。

その後はDOSでバッチファイルを書く程度でこのブログを始めるまでは、長らくプログラムをするということは遠のいていました。

参考にしたサイト


LibreOffice 5.3 SDK - Examples
このC++のcomplextoolbarcontrolsの例をデバッガにかけました。

GDBの使い方 - PukiWiki
ブレークポイントの設定の仕方がとても参考になりました。

What is SAL_CALL in c++? - Stack Overflow
SAL_CALLというのはLibreOfficeで定義されている文字列置換のマクロでした。

[roast] 2012/5/19 -> gccとg++って、厳密には違うらしいね。 | myun2's blog
gccとg++は似たようなもののようです。

基本を再チェック~gcc~@謎の処理系 SunOS 4.1.4
C++で書いてあってもgccでコンパイルできるものがあるようです。

DebugBreakとプロセスアタッチでC++アプリケーションをデバッグする - 大人になったら肺呼吸
WindowsでブレークポイントをC++のソースに埋め込む方法。

Cのプログラムの中でブレークポイントを設定する - bkブログ
Windows以外でブレークポイントをCのソースに埋め込む方法。

GitHub - scottt/debugbreak: break into the debugger programmatically
C++のソースにブレークポイントを埋め込む汎用的なライブラリ。

次の関連記事:linuxBean14.04(163)OUStringの文字列をgdbで見る方法

PR

0 件のコメント:

コメントを投稿