python import re,subprocess class my_gdb_func(gdb.Function): __slots__="invoke", def make_my_gdb_func(f): my_gdb_func(f.__name__).invoke=f class my_gdb_cmd(gdb.Command): __slots__="invoke", def make_my_gdb_cmd(f): my_gdb_cmd(f.__name__,gdb.COMMAND_USER).invoke=f log=[] valid_QQmlElements=set() valid_QQuickMenus=set() valid_QQuickMenuPrivates=set() processEvents_counter=0 mouse_press_counter=0 QObject_deleteLater_addr=0 QQuickMenu_counter=0 @make_my_gdb_func def log_create_QQmlElement(this): this=int(this) log.append((processEvents_counter,"create QQmlPrivate::QQmlElement",this)) if this in valid_QQmlElements: gdb.write("bad create QQmlPrivate::QQmlElement\n") return True valid_QQmlElements.add(this) return False @make_my_gdb_func def log_destroy_QQmlElement(this): this=int(this) log.append((processEvents_counter,"destroy QQmlPrivate::QQmlElement",this)) try: valid_QQmlElements.remove(this) except KeyError: gdb.write("bad destroy QQmlPrivate::QQmlElement\n") return True return False @make_my_gdb_func def log_create_QQuickMenu(this,parent): this=int(this) log.append((processEvents_counter,"create QQuickMenu",this,int(parent))) if this in valid_QQuickMenus: gdb.write("bad create QQuickMenu\n") return True valid_QQuickMenus.add(this) global QQuickMenu_counter QQuickMenu_counter+=1 return False @make_my_gdb_func def log_create_QQuickMenuPrivate(this,parent): this=int(this) log.append((processEvents_counter,"create QQuickMenuPrivate",this,int(parent))) if this in valid_QQuickMenus: gdb.write("bad create QQuickMenuPrivate\n") return True valid_QQuickMenuPrivates.add(this) return False @make_my_gdb_func def handle_QQuickMenuPrivate_get_ret(result): return int(result) not in valid_QQuickMenuPrivates @make_my_gdb_func def log_QQuickMenuPrivate_setParentMenu(this,parent): this=int(this) parent=int(parent) log.append((processEvents_counter,"QQuickMenuPrivate::setParentMenu",this,parent)) if (this not in valid_QQuickMenuPrivates) or (parent and parent not in valid_QQuickMenus): gdb.write("bad QQuickMenuPrivate::setParentMenu\n") return True return False @make_my_gdb_func def log_destroy_QQuickMenuPrivate(this): this=int(this) log.append((processEvents_counter,"destroy QQuickMenuPrivate",this)) try: valid_QQuickMenuPrivates.remove(this) except KeyError: gdb.write("bad destroy QQuickMenuPrivate\n") return True return False @make_my_gdb_func def log_destroy_QQuickMenu(this): this=int(this) log.append((processEvents_counter,"destroy QQuickMenu",this)) try: valid_QQuickMenus.remove(this) except KeyError: gdb.write("bad destroy QQuickMenu\n") return True return False @make_my_gdb_func def log_processEvents_begin(): global processEvents_counter processEvents_counter+=1 return False @make_my_gdb_func def log_mouse_press(): log.append((processEvents_counter,"mouse press")) global mouse_press_counter mouse_press_counter+=1 # if mouse_press_counter==2: # gdb.execute(f"break *{QObject_deleteLater_addr} thread 1 if {interesting_QQuickMenu_addr}==$rdi") return False @make_my_gdb_cmd def generalize_log(arg,from_tty): what2categories={ "create QQmlPrivate::QQmlElement":("QQmlElement",), "destroy QQmlPrivate::QQmlElement":("QQmlElement",), "create QQuickMenu":("QQuickMenu","unknown"), "create QQuickMenuPrivate":("QQuickMenuPrivate",), "QQuickMenuPrivate::setParentMenu":("QQuickMenuPrivate","QQuickMenu"), "destroy QQuickMenuPrivate":("QQuickMenuPrivate",), "destroy QQuickMenu":("QQuickMenu",), "mouse press":(), "generalize log":() } addr_simplify_table={} category_counter_table={} def simplify_addr(addr,category): if category=="int": return " int(%d)"%addr if not addr: return " NULL" try: result=addr_simplify_table[addr] except KeyError: category_counter=category_counter_table.setdefault(category,[0]) category_counter[0]+=1 result=category,category_counter[0] addr_simplify_table[addr]=result else: if category!="unknown" and result[0]!=category: raise Exception("wrong category") return " %s_%d"%result rpec=0#real processEvents counter log.append((processEvents_counter,"generalize log")) with open(arg,"w") as outfile: for rpec2,what,*addrs in log: if rpec!=rpec2: rpec=rpec2 outfile.write("== QEventDispatcherGlib::processEvents ==\n") outfile.write(what) outfile.writelines(map(simplify_addr,addrs,what2categories[what])) outfile.write("\n") outfile.write("\n== name = address ==\n") for addr,[category,index] in addr_simplify_table.items(): outfile.write("%s_%d = %#x\n"%(category,index,addr)) def get_symbols(file_path): r=subprocess.run(("objdump","-wt","--",file_path),stdout=subprocess.PIPE) r.check_returncode() return {m[3]:(int(m[1],16),int(m[2],16)) for m in re.finditer(br"^([0-9a-f]+) .{7} \.text\t([0-9a-f]+) +(.+)$",r.stdout,re.MULTILINE)} @make_my_gdb_func def handle_main(): libs={m[2][m[2].rindex("/")+1:]:(int(m[1],16),m[2]) for m in re.finditer(r"^0x([0-9a-f]+) {2}0x[0-9a-f]+ {2}.{7} {5}(.+)$",gdb.execute("info dll",False,True),re.MULTILINE)} libQt6QuickTemplates2_real_text_addr,libQt6QuickTemplates2_path=libs["libQt6QuickTemplates2.so.6"] libQt6QuickTemplates2_syms=get_symbols(libQt6QuickTemplates2_path) libQt6QuickTemplates2_add=libQt6QuickTemplates2_real_text_addr-libQt6QuickTemplates2_syms[b".text"][0] libQt6Core_real_text_addr,libQt6Core_path=libs["libQt6Core.so.6"] libQt6Core_syms=get_symbols(libQt6Core_path) libQt6Core_add=libQt6Core_real_text_addr-libQt6Core_syms[b".text"][0] global QObject_deleteLater_addr QObject_deleteLater_addr=libQt6Core_syms[b"_ZN7QObject11deleteLaterEv"][0]+libQt6Core_add gdb.execute(f"""disable 1 break *{libQt6QuickTemplates2_syms[b".hidden _ZN11QQmlPrivate11QQmlElementI14QQuickMenuItemEC2Ev"][0]+libQt6QuickTemplates2_add} thread 1 if $log_create_QQmlElement($rdi) break *{libQt6QuickTemplates2_syms[b".hidden _ZN11QQmlPrivate11QQmlElementI14QQuickMenuItemED2Ev"][0]+libQt6QuickTemplates2_add} thread 1 if $log_destroy_QQmlElement($rdi) break *{libQt6QuickTemplates2_syms[b"_ZN10QQuickMenuC2EP7QObject"][0]+libQt6QuickTemplates2_add} thread 1 if $log_create_QQuickMenu($rdi,$rsi) break *{libQt6QuickTemplates2_syms[b"_ZN17QQuickMenuPrivateC2Ev"][0]+libQt6QuickTemplates2_add} thread 1 if $log_create_QQuickMenuPrivate($rdi,$rsi) break *{sum(libQt6QuickTemplates2_syms[b".hidden _ZN17QQuickMenuPrivate3getEP10QQuickMenu"],libQt6QuickTemplates2_add-1)} thread 1 if $handle_QQuickMenuPrivate_get_ret($rax) break *{libQt6QuickTemplates2_syms[b"_ZN17QQuickMenuPrivate13setParentMenuEP10QQuickMenu"][0]+libQt6QuickTemplates2_add} thread 1 if $log_QQuickMenuPrivate_setParentMenu($rdi,$rsi) break *{libQt6QuickTemplates2_syms[b".hidden _ZN17QQuickMenuPrivateD2Ev"][0]+libQt6QuickTemplates2_add} thread 1 if $log_destroy_QQuickMenuPrivate($rdi) break *{libQt6QuickTemplates2_syms[b"_ZN10QQuickMenuD2Ev"][0]+libQt6QuickTemplates2_add} thread 1 if $log_destroy_QQuickMenu($rdi) break *{libQt6Core_syms[b"_ZN20QEventDispatcherGlib13processEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE"][0]+libQt6Core_add} thread 1 if $log_processEvents_begin() break -source qguiapplication.cpp -line 2405 thread 1 if $log_mouse_press()""") return False #break -source qquickloader.cpp -line 319 thread 1 end set environment LD_PRELOAD libQt6QuickTemplates2.so.6 set environment LD_LIBRARY_PATH /home/asd/s/qt/b/lib/x86_64-linux-gnu:/home/asd/s/qd/b/lib/x86_64-linux-gnu set environment QT_PLUGIN_PATH /home/asd/s/qt/b/lib/x86_64-linux-gnu/qt6/plugins:/home/asd/s/qd/b/lib/x86_64-linux-gnu/qt6/plugins set environment QML_IMPORT_PATH /home/asd/s/qd/b/lib/x86_64-linux-gnu/qt6/qml set environment QV4_FORCE_INTERPRETER 1 file /home/asd/s/qd/b/lib/qt6/bin/qml break -function main if $handle_main() run /home/asd/Documents/qmltest/test.qml