Python에서 C용 LLDB 사용자 정의 데이터 포맷 프로그램

13571 단어 pythonlldbdebuggingc

소개하다.
LLDB 디버그 C나 C++ 프로그램을 사용하면 LLDB의 인쇄 (p) 명령의 출력을 기본값이 아닌 print the contents of variables in a more enlightening way으로 사용자 정의할 수 있습니다.너는 심지어 Python을 사용할 수 있다.그러나 내가 찾을 수 있는 examples은 거의 상대적으로 자질구레한 데이터 구조만 보였다.대량의 검색과 시도, 오류를 통해 나는 파이톤을 사용하여 평범하지 않은 struct을 인쇄하는 방법을 찾았다.

데이터 구조
인쇄하고 싶은 C 데이터 구조는 cdecl, the following을 포함하여 단일 체인 테이블(이 예에서 일부 조정됨)을 구현합니다.
typedef struct slist      slist_t;
typedef struct slist_node slist_node_t;

struct slist {
  slist_node_t *head;           // Pointer to list head.
  slist_node_t *tail;           // Pointer to list tail.
};

struct slist_node {
  slist_node_t *next;           // Pointer to next node or null.
  void         *data;           // Pointer to user data.
};
그런 다음 S::T과 같이 C++ 범위의 이름을 저장하는 데 사용합니다.
typedef struct slist c_sname_t; // C++ scoped name.
그 중에서 각 역할 영역의 이름은 slist_node으로 저장되고 그 중에서 data은 실제로는 c_scope_data*이다. 그 중에서 c_scope_data은 각 역할 영역에 대한 데이터를 포함하고 특히 그 이름은 다음과 같다.
struct c_scope_data {
    char const *name;           // The scope's name.
    // ...
};
S::T의 경우 slist은 두 개의 노드를 포함한다. 하나는 S에 사용되고 다른 하나는 T에 사용된다.
LLDB에서 c_sname_t의 변수를 디버깅하고 인쇄할 때, 나는 LLDB가 전체 작용 도메인 이름 S::T을 인쇄하기를 희망한다. 즉, 각 노드의 이름 목록을 인쇄하는 것이지, head 자체의 tailslist 바늘의 기본값만 인쇄하는 것이 아니다.

LLDB Python 모듈
이를 위해 LLDB Python 모듈을 구현했습니다.이 모듈은 다음부터 시작합니다.
# cdecl_lldb.py

import lldb

def __lldb_init_module(debugger, internal_dict):
    cmd_prefix = 'type summary add -F ' + __name__
    debugger.HandleCommand(cmd_prefix + '.show_c_sname_t c_sname_t')
__lldb_init_moduleLLDB 함수를 호출하여 모듈을 초기화합니다. 그 중에서 debugger은 LLDB 자체의 실례이고 서명은 internal_dict이 필요하지만 상호작용을 할 필요가 없습니다.이 함수를 사용하여 C 유형을 사용자 정의 포맷 프로그램에 바인딩할 수 있습니다.
대사:
    cmd_prefix = 'type summary add -F ' + __name__
    debugger.HandleCommand(cmd_prefix + '.show_c_sname_t c_sname_t')
cmd_prefix을 후속 줄에 사용할 줄임말(그중 __name__은 현재 Python 파일의 이름이고 .py 확장자가 없음)으로 성명하였으며, HandleCommand은 포맷 프로그램(-F)을 show_c_sname_t 형식의 c_sname_t 함수에 귀속시켰다.(네, typedef개 d명칭 가능합니다.)show_c_sname_t을 실현하기 전에 우리는 데이터 구조를 두루 훑어보는 데 도움을 줄 실용 함수가 필요하다.
def null(ptr):
    """Gets whether the SBValue is a NULL pointer."""
    return not ptr.IsValid() or ptr.GetValueAsUnsigned() == 0
(SBValue은 변수, 레지스터 또는 표현식의 내용을 저장하는 LLDB 데이터 구조입니다.)show_c_sname_t의 구축 시작:
def show_c_sname_t(c_sname, internal_dict):
    colon2 = False          # Print "::" scope separator?
    rv = ""                 # "string-ified" value to return
c_sname_ttypedefslist이기 때문에 우리는 먼저 목록의 head을 획득한 다음에 목록을 훑어보아야 한다.공교롭게도 SBValue에는 linked_list_iter() 함수가 있는데 다음 지침을 포함하는 struct의 구성원의 이름은 다음과 같다.
    head = c_sname.GetChildMemberWithName('head')
    for slist_node_ptr in head.linked_list_iter('next'):
각 노드에 대해 data의 구성원을 확보한 다음에 void*c_scope_data_t*으로 전환해야 한다.전환을 진행하려면 파이톤에서 c_scope_data_t* 형식을 가져와야 합니다.이 작업은 다음 방법으로 순환하기 전에 한 번 완료할 수 있습니다.
    target = lldb.debugger.GetSelectedTarget()
    c_scope_data_ptr_t = target.FindFirstType('c_scope_data_t').GetPointerType()
이제 이 기능의 나머지 부분은 다음과 같습니다.
    head = c_sname.GetChildMemberWithName('head')
    for slist_node_ptr in head.linked_list_iter('next'):
        void_data_ptr = slist_node_ptr.GetChildMemberWithName('data')
        if not null(void_data_ptr):
            c_scope_data_ptr = void_data_ptr.Cast(c_scope_data_ptr_t)
            name_ptr = c_scope_data_ptr.GetChildMemberWithName('name')
            if not null(name_ptr):
                if colon2:
                    rv += '::'
                else:
                    colon2 = True
                rv += name_ptr.GetSummary().strip('"')

    return '"' + rv + '"'
name_ptr을 지정하고, 우리는 GetSummary()을 사용하여 실제 명칭 문자열을 가져옵니다.GetSummary()은 결과 주위에 인용 부호를 붙인다. 우리는 모든 역할 영역의 이름이 아니라 전체 반환 값 주위에 인용 부호를 붙이기를 원하기 때문이다.

로드 모듈
LLDB가 시작할 때마다 모듈을 불러오기 위해서 저는 먼저 cdecl의 .lldbinit 디렉터리에 src 파일을 만들었습니다.
# .lldbinit
command script import cdecl_lldb.py
LLDB를 로드하려면 allow loading of .lldbinit files from the current working directory이 필요합니다.이렇게 하려면 다음을 추가합니다.
settings set target.load-cwd-lldbinit true
전 세계 ~/.lldbinit 파일

대화식으로 Python 사용
올바른 Python을 생성하려고 시도할 때, 스크립트를 편집하고, 저장하고, LLDB를 시작하고, 인터럽트를 설정하고, 프로그램을 실행할 때마다 ("편집 - 컴파일 - 실행 순환"), LLDB에서 Python 해석기에 들어가서, 변수의 정확한 Python 코드를 인쇄할 수 있도록 반복 테스트를 할 수 있습니다.예를 들어, 다음 컨텐트가 포함된 함수를 지정합니다.
c_sname_t const *sname = find_name( /*...*/ );
do_something( sname );
do_something()에서 인터럽트를 설정하고 프로그램을 실행할 수 있습니다.인터럽트가 발생하여 LLDB에 넣으면 script 명령을 통해 Python 해석기에 추가할 수 있습니다.
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>>
우선 Python에서 sname 변수를 가져오는 것이 가장 중요합니다. 더 구체적으로 말하면 현재 창고 프레임워크에서 가져오는 것은 다음과 같습니다.
>>> sname = lldb.frame.FindVariable('sname')
현재, Python 코드를 상호작용으로 시도할 수 있으며, 편집 컴파일 실행 순환을 단축할 수 있습니다.

좋은 웹페이지 즐겨찾기