web-dev-qa-db-ja.com

C ++デバッグ/ GDBを使用したカスタムタイプの印刷:nlohmann jsonライブラリの場合

nlohmannのjson C++実装 を使用してプロジェクトに取り組んでいます。

GDBでnlohmannのJSONキー/値を簡単に探索するにはどうすればよいですか?

これを使用しようとしました STL gdb wrapping これは、nlohmannのJSON libが使用している標準C++ライブラリ構造を探索するためのヘルパーを提供するためです。しかし、私はそれが便利だとは思いません。

簡単な使用例を次に示します。

json foo;
foo["flex"] = 0.2;
foo["awesome_str"] = "bleh";
foo["nested"] = {{"bar", "barz"}}; 

GDBに入れたいもの:

(gdb) p foo
{
    "flex" : 0.2,
    "awesome_str": "bleh",
    "nested": etc.
}

現在の行動

(gdb) p foo
$1 = {
  m_type = nlohmann::detail::value_t::object, 
  m_value = {
    object = 0x129ccdd0, 
    array = 0x129ccdd0, 
    string = 0x129ccdd0, 
    boolean = 208, 
    number_integer = 312266192, 
    number_unsigned = 312266192, 
    number_float = 1.5427999782486669e-315
  }
}
(gdb) p foo.at("flex")
Cannot evaluate function -- may be inlined // I suppose it depends on my compilation process. But I guess it does not invalidate the question.
(gdb) p *foo.m_value.object
$2 = {
  _M_t = {
    _M_impl = {
      <std::allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer> > > >> = {
        <__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer> > > >> = {<No data fields>}, <No data fields>}, 
      <std::_Rb_tree_key_compare<std::less<void> >> = {
        _M_key_compare = {<No data fields>}
      }, 
      <std::_Rb_tree_header> = {
        _M_header = {
          _M_color = std::_S_red, 
          _M_parent = 0x4d72d0, 
          _M_left = 0x4d7210, 
          _M_right = 0x4d7270
        }, 
        _M_node_count = 5
      }, <No data fields>}
  }
}
18
LoneWanderer

print of std :: string に関するGDB機能とスタックオーバーフローの質問を読んで、自分の答えを見つけました。 short pathが今のところ最良のオプションです。

ショートパスv3.1.2

私は単に次のようにgdbコマンドを定義しました:

# this is a gdb script
# can be loaded from gdb using
# source my_script.txt (or. gdb or whatever you like)
define pjson
# use the lohmann's builtin dump method, ident 4 and use space separator
printf "%s\n", $arg0.dump(4, ' ', true).c_str()
end
# configure command helper (text displayed when typing 'help pjson' in gdb)
document pjson
Prints a lohmann's JSON C++ variable as a human-readable JSON string
end

Gdbで使用する:

(gdb) source my_custom_script.gdb
(gdb) pjson foo
{
    "flex" : 0.2,
    "awesome_str": "bleh",
    "nested": {
        "bar": "barz"
    }
}

ショートパスv3.7.0 [編集] 2019-onv-06新しいto_string()メソッドを使用することもできますが、ライブの劣ったプロセスでGDBを使用して動作させることができませんでした。以下の方法はまだ機能します。

# this is a gdb script
# can be loaded from gdb using
# source my_script.txt (or. gdb or whatever you like)
define pjson
# use the lohmann's builtin dump method, ident 4 and use space separator
printf "%s\n", $arg0.dump(4, ' ', true, json::error_handler_t::strict).c_str()
end
# configure command helper (text displayed when typing 'help pjson' in gdb)
document pjson
Prints a lohmann's JSON C++ variable as a human-readable JSON string
end

オーバーザ(しかし私には役に立たない)

もう1つの方法は、GDBpretty printerをpythonで定義し、プロジェクトに密接に関連付けることです(オートローディングスタッフ)詳細なアプローチについては このリンク を参照してください。

基本的に、gdbで次のように入力します。

(gdb) p foo

そして、GDBは_fooのタイプを自動的にテストし、関連するプリティプリンターがあればそれを呼び出します。それは同じ結果になるでしょう。主な違いは、よく知られているprintコマンドを使用して行われることであり、さらに重要なのは、from(精度に感謝ロシア語を採用)。デバッグする人は、新しいコマンド(短い答えで定義されたpjsonのような)を学ぶ必要はありません。

以下に、いくつかのGDBドキュメントの抜粋+ python機能しないコードの試み。


引用:

Pretty-printerは2つの部分で構成されます。タイプがサポートされているかどうかを検出するルックアップ関数と、プリンター自体です。

以下は、std::stringプリンターの記述例です。このクラスが提供する必要があるAPIの詳細については、 Pretty Printing API を参照してください。

class StdStringPrinter(object):
    "Print a std::string"

    def __init__(self, val):
        self.val = val

    def to_string(self):
        return self.val['_M_dataplus']['_M_p']

    def display_hint(self):
        return 'string'

完全を期すために引用します:

上記のプリンターの例のルックアップ関数を作成する方法を示す例を次に示します。

def str_lookup_function(val):
    lookup_tag = val.type.tag
    if lookup_tag == None:
        return None
    regex = re.compile("^std::basic_string<char,.*>$")
    if regex.match(lookup_tag):
        return StdStringPrinter(val)
    return None

この方法で実装してみました。しかし、私は次のコードで100%の失敗率があり、不可解なGDBエラーメッセージが表示されます(コードサンプルの下を参照)

注:Value.Typeチェックをバイパスして、GDB内でC++クラスメソッドの呼び出しを許可することになっている ここで提供されるトリック に依存しています(オブジェクトメソッドが見つかり、そのvalue.Typegdb.TYPE_CODE_METHODになりますが、gdb pythonは呼び出し可能とは見なしません。呼び出し可能なのはgdb.TYPE_CODE_FUNCのみです。したがって、実際のメソッドを実行するためのハックとしてparse_and_evalactsコール)。

import gdb
import re

class StdStringPrinter(object):
    """Print a std::string"""
    def __init__(self, val):
        self.val = val
    def to_string(self):
        eval_string = "(*("+str(self.val.type)+"*)("+str(self.val.address)+")).c_str()" # works 50% of the time ...
        return gdb.parse_and_eval(eval_string)
    def display_hint(self):
        return 'string'

class LohmannJSONPrinter(object):
    """Print a nlohmann::json"""
    def __init__(self, val):
        self.val = val
    def to_string(self):

        # workaround from here:
        # https://stackoverflow.com/a/22798055/7237062
        # "(*("+str(self.val.type)+"*)("+str(self.val.address)+")).method()"
        eval_string = '(*('+str(self.val.type)+'*)('+str(self.val.address)+')).dump(4, " ", true)'
        return gdb.parse_and_eval(eval_string) # fails 100% of the time
    def display_hint(self):
        return self.val.type

def build_pretty_printer():
    pp = gdb.printing.RegexpCollectionPrettyPrinter("foo")
    json = r"nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer>"
    pp.add_printer('nlohmann::json', json, LohmannJSONPrinter)
    return pp

# executed at autoload gdb.printing.register_pretty_printer(gdb.current_objfile(),
                                     build_pretty_printer())

エラー:

Cannot insert breakpoint -18. // or any negative value
Cannot access memory at address 0x111a2180 // appears to be a fixed value at each execution
Python Exception <class 'gdb.error'> Command aborted.

または

$2 = Python Exception <class 'gdb.error'> Attempt to take address of value not located in memory.:

2019年3月24日を編集:雇用されたロシア語によって与えられる精度を追加します。

24
LoneWanderer

私の解決策は〜/ .gdbinitファイルを編集することでした。

define jsontostring 
   printf "%s\n", $arg0.dump(2, ' ', true, nlohmann::detail::error_handler_t::strict).c_str()
end

これにより、「jsontostring」コマンドは、ファイルを調達する必要なく、すべてのgdbセッションで利用可能になります。

(gdb) jsontostring object

0
Lexusminds