2023-07-27 16:32:51來源:Linux二進制
作為一名Linux系統(tǒng)下的C語言開發(fā),經常需要閱讀源碼,但是有些源碼實在是難以閱讀,各種龐大的結構體交雜,分分鐘把你繞暈,讓你頭昏眼花,遲遲無法梳理清楚。這時候,一個能夠幫你梳理數(shù)據(jù)結構的工具就顯得極其重要,讓你能夠很清晰的看出各個數(shù)據(jù)結構之間的關系。
本文我們主要介紹CentOS平臺下通過python和graphviz生成數(shù)據(jù)結構關系圖。
一、前置條件為使用python和graphviz生成C語言的數(shù)據(jù)結構關系圖,需提前安裝好python3,這里不做介紹。這里介紹一下繪圖工具graphviz和Linux命令行打開圖片的工具eog等。
(資料圖片)
Graphviz(Graph Visualization Software)是一個由AT&T實驗室啟動的開源工具包,能夠支持基于 DOT 腳本,文件擴展名通常是 .gv 或 .dot 的描述繪制圖形。DOT 是一種文本圖形描述語言,將生成的圖形轉換成多種輸出格式的命令行工具,其輸出格式包括PostScript,PDF,SVG,PNG,含注解的文本等。DOT 本身非常原始,提供了一種非常簡單的描述圖形的方法,同時意味著可以在命令行終端使用,或者被其它編程語言調用(Graphviz 就可以作為一個庫使用)。這一點非常關鍵,基于 Graphviz 應用開發(fā)者不必掌握布局的復雜算法,而是可以把精力放在業(yè)務方面,將最后的圖對象交給繪圖引擎來處理即可。
yum install graphviz -y
2、安裝命令行圖片查看工具GNOME之眼,圖像查看器(eog)是GNOME桌面的官方圖像查看器,可以用來在服務器端查看圖片。它可以查看各種格式的單個圖像文件,以及大型圖像集合。eog可以通過插件系統(tǒng)進行擴展。
yum install eog -y
二、生成工具及使用方法繪制關鍵數(shù)據(jù)結構的關聯(lián)關系圖,可以協(xié)助我們快速理解組織架構,加速理解代碼邏輯;Linux平臺下生成C語言數(shù)據(jù)結構關系圖主要基于python+graphviz,python和graphviz工具是基礎,需要輔助以python腳本,才能實現(xiàn)分析數(shù)據(jù)結構并生成用于繪圖的dot語言;之后利用graphviz根據(jù)上一步中的臨時生成文件的dot語言描述繪圖。圖形保存到xxx.svg文件中,.svg可以使用eog或者瀏覽器打開。
1、Python腳本分析工具用于分析結構體關聯(lián)關系的python腳本(analysis_dt.py),如下:
#!/usr/bin/python3import os,reprefix = """digraph spdk { graph [ rankdir = "LR" //splines=polyline //overlap=false ]; node [ fontsize = "16" shape = "ellipse"\r ]; edge [ ];"""middle_str = ""edge_list = []edge_string = ""cur_indentation_level = 0space4 = " "space8 = space4 + space4space12 = space4 + space8space16 = space4 + space12node_database = {}node_database["created"] = []color_arrary = ["red", "green", "blue", "black","blueviolet","brown", "cadetblue","chocolate","crimson","cyan","darkgrey","deeppink",data_structwith open(r"/tmp/713/data_struct", "r") as file_input: tmpline = file_input.readline() while(tmpline): tmpline = re.sub(r"([^a-zA-Z0-9]const )", " ", tmpline) #for match :struct device { if re.search(r"struct\s*([0-9a-zA-Z_\-]+)\s*\{", tmpline): m = re.search(r"struct\s*([0-9a-zA-Z_\-]+)\s*\{", tmpline) cur_indentation_level += 1 if (cur_indentation_level == 1): node_name = m.group(1) node_str = space4 + "\"" + node_name + "\" [\n" + space8 + "label = \" "+ node_name +"\l|\n" + space12 + "{|{\n" node_database["created"].append(node_name) try: node_database[node_name]["node_str"] = node_str except: node_database[node_name] = {} node_database[node_name]["node_str"] = node_str #for match :struct device *parent; elif re.search(r"struct\s*([0-9a-zA-Z_\-]+)\s*(\**)(\s*)([0-9a-zA-Z_\-]+)\s*;", tmpline) and cur_indentation_level > 0: m = re.search(r"struct\s*([0-9a-zA-Z_\-]+)\s*(\**)(\s*)([0-9a-zA-Z_\-]+)\s*;", tmpline) member_type = m.group(1) node_database[node_name]["node_str"] += space16 + "<"+ member_type + "> " + m.group(2) + m.group(3) + m.group(4) + "\l|\n" try: node_database[member_type]["included_by"].append(node_name) except: try: node_database[member_type]["included_by"] = [] node_database[member_type]["included_by"].append(node_name) except: node_database[member_type] = {} node_database[member_type]["included_by"] = [] node_database[member_type]["included_by"].append(node_name) #print("%s included by %s"%(member_type, node_database[member_type]["included_by"])) if(member_type in node_database["created"]): tmp_edge_str = space4 + node_name + ":" + member_type + " -> " + member_type + ":" + "head" if not tmp_edge_str in edge_list: edge_list.append(tmp_edge_str) #for match : void *driver_data; elif re.search(r"\s*[0-9a-zA-Z_\-]+\s*(\**[0-9a-zA-Z_\-]+)\s*;", tmpline) and cur_indentation_level > 0: m = re.search(r"\s*[0-9a-zA-Z_\-]+\s*(\**[0-9a-zA-Z_\-]+)\s*;", tmpline) node_database[node_name]["node_str"] += space16 + "<"+ m.group(1) + "> " + m.group(1) + "\l|\n" #for match:const char *init_name; elif re.search(r"(.*)\s+(\**)(\s*)([0-9a-zA-Z_\-]+\s*);", tmpline) and cur_indentation_level > 0: m = re.search(r"(.*)\s+(\**)(\s*)([0-9a-zA-Z_\-]+\s*);", tmpline) node_database[node_name]["node_str"] += space16 + "<"+ m.group(2) + "> " + m.group(2) + m.group(3) + m.group(4) + "\l|\n" #for match:int *(*runtime_idle)(struct device *dev); elif re.search(r"\s*[0-9a-zA-Z_\-]+\s*\**\s*\(\s*(\**\s*[0-9a-zA-Z_\-]+)\s*\)\s*\([^\)]*\)\s*;", tmpline) and cur_indentation_level > 0: m = re.search(r"\s*[0-9a-zA-Z_\-]+\s*\**\s*\(\s*(\**\s*[0-9a-zA-Z_\-]+)\s*\)\s*\([^\)]*\)\s*;", tmpline) node_database[node_name]["node_str"] += space16 + "<"+ m.group(1) + "> (" + m.group(1) + ")\l|\n" #for match: }; elif re.search(r"\s*\}\s*;", tmpline): if(cur_indentation_level >= 1): cur_indentation_level -= 1 if (cur_indentation_level == 0): node_database[node_name]["node_str"] += space12 + "}}\"\n" node_database[node_name]["node_str"] += space8 + "shape = \"record\"\n" + space4 + "];\n" if "included_by" in node_database[node_name]: for parent_node in node_database[node_name]["included_by"]: if parent_node in node_database["created"]: tmp_edge_str = space4 + parent_node + ":" + node_name + " -> " + node_name + ":" + "head" if not tmp_edge_str in edge_list: edge_list.append(tmp_edge_str) tmpline = file_input.readline()for tmpnode in node_database["created"]: middle_str = middle_str + node_database[tmpnode]["node_str"]for i, tmpstr in enumerate(edge_list): edge_string += tmpstr + "[color="" + color_arrary[i%len(color_arrary)] + ""]\n"print(prefix + middle_str + "\n" + edge_string + "}")
2、使用方法繪制C語言結構體關系圖方法和流程如下:
(1)把需要繪制關系圖的關鍵數(shù)據(jù)結構復制粘貼到一個文本文件data_struct中;
(2)把python腳本中保存數(shù)據(jù)結構的文件路徑(/tmp/713/data_struct )替換為自己的保存數(shù)據(jù)結構的文件路徑(可自行修改腳本,通過參數(shù)傳入文件路徑);
(3)執(zhí)行命令,自動生成關系圖,命令如下:
python3 analysis_dt.py > tmpfiledot -Tsvg tmpfile -o xxx.svg
其中第一條命令使用python分析數(shù)據(jù)結構并生成用于繪圖的dot語言,第二條命令利用graphviz根據(jù)tmpfile中的dot語言描述繪圖。圖形保存到xxx.svg文件中,xxx可以自行命名;生成的xxx.svg文件可以在服務器的命令行使用eog打開,也可以下載到windows上使用瀏覽器打開,且可以實現(xiàn)縮放。
注意:這里也可以通過dot命令直接生成圖片格式,如下:
dot -Tsvg tmpfile -o xxx.png即可生成xxx.png圖片。
這里以一個簡單的結構體文本文件data_struct為Demo,查看其內結構體之間的關系。data_struct文本文件內容如下:
struct ovsdb { char *name; struct ovsdb_schema *schema; struct ovsdb_storage *storage; /* If nonnull, log for transactions. */ struct uuid prereq; struct ovs_list monitors; /* Contains "struct ovsdb_monitor"s. */ struct shash tables; /* Contains "struct ovsdb_table *"s. */ /* Triggers. */ struct ovs_list triggers; /* Contains "struct ovsdb_trigger"s. */ bool run_triggers; bool run_triggers_now; struct ovsdb_table *rbac_role; /* History trasanctions for incremental monitor transfer. */ bool need_txn_history; /* Need to maintain history of transactions. */ unsigned int n_txn_history; /* Current number of history transactions. */ unsigned int n_txn_history_atoms; /* Total number of atoms in history. */ struct ovs_list txn_history; /* Contains "struct ovsdb_txn_history_node. */ size_t n_atoms; /* Total number of ovsdb atoms in the database. */ /* Relay mode. */ bool is_relay; /* True, if database is in relay mode. */ /* List that holds transactions waiting to be forwarded to the server. */ struct ovs_list txn_forward_new; /* Hash map for transactions that are already sent and waits for reply. */ struct hmap txn_forward_sent; /* Database compaction. */ struct ovsdb_compaction_state *snap_state;};struct ovsdb_storage { /* There are three kinds of storage: * * - Standalone, backed by a disk file. "log" is nonnull, "raft" is * null. * * - Clustered, backed by a Raft cluster. "log" is null, "raft" is * nonnull. * * - Memory only, unbacked. "log" and "raft" are null. */ struct ovsdb_log *log; struct raft *raft; char *unbacked_name; /* Name of the unbacked storage. */ /* All kinds of storage. */ struct ovsdb_error *error; /* If nonnull, a permanent error. */ long long next_snapshot_min; /* Earliest time to take next snapshot. */ long long next_snapshot_max; /* Latest time to take next snapshot. */ /* Standalone only. */ unsigned int n_read; unsigned int n_written;};struct ovsdb_table { struct ovsdb_table_schema *schema; struct ovsdb_txn_table *txn_table; /* Only if table is in a transaction. */ struct hmap rows; /* Contains "struct ovsdb_row"s. */ /* An array of schema->n_indexes hmaps, each of which contains "struct * ovsdb_row"s. Each of the hmap_nodes in indexes[i] are at index "i" at * the end of struct ovsdb_row, following the "fields" member. */ struct hmap *indexes; bool log; /* True if logging is enabled for this table. */};struct ovsdb_compaction_state { pthread_t thread; /* Thread handle. */ struct ovsdb *db; /* Copy of a database data to compact. */ struct json *data; /* "db" as a serialized json. */ struct json *schema; /* "db" schema json. */ uint64_t applied_index; /* Last applied index reported by the storage * at the moment of a database copy. */ /* Completion signaling. */ struct seq *done; uint64_t seqno; uint64_t init_time; /* Time spent by the main thread preparing. */ uint64_t thread_time; /* Time spent for compaction by the thread. */};
執(zhí)行結果如下:
[root@localhost 713]# vi analysis_dt.py[root@localhost 713]# python3 analysis_dt.py > tmpfile[root@localhost 713]# lsanalysis_dt.py data_struct tmpfile[root@localhost 713]# vi tmpfile[root@localhost 713]# dot -Tsvg tmpfile -o my.svg[root@localhost 713]# lsanalysis_dt.py data_struct my.svg tmpfile[root@localhost 713]# eog my.svg
eog打開my.svg文件,結果如下:
圖片
根據(jù)圖片可以看出,成功展現(xiàn)了結構體之間的關系。
三、總結graphviz很強大,可以用來畫各種各樣的圖,本文只是簡單總結一下用于畫C語言結構體關系的方法,并做一個記錄。小編在看代碼的時候,一直想著有什么工具可以實現(xiàn)這個功能,檢索了一圈,沒找到什么很方便的工具。也有人推薦用draw.io,processon等,但是小編覺得這兩個工具畫結構體關系圖終究是沒有這種通過腳本工具的方法方便。因此總結成文,希望對有需要的朋友能夠有幫助,也希望有更方便的解決方案的朋友可以反饋給小編,大家一起互幫互助,一起成長。
關鍵詞:
作為一名Linux系統(tǒng)下的C語言開發(fā),經常需要閱讀源碼,但是有些源碼實在
1 介紹圖1展現(xiàn)了本文所涉及的新興硬件安全技術、以及新興硬件安全技術
本文經AI新媒體量子位(公眾號ID:QbitAI)授權轉載,轉載請聯(lián)系出處。
就在剛剛,StabilityAI正式發(fā)布了下一代文生圖模型——SDXL1 0。要知道
「程序合成」或「代碼生成」任務的目標是根據(jù)給定的描述生成可執(zhí)行代碼
HTML5可以在文檔中使用MathML元素,對應的標簽是 。MathML是數(shù)學標記
以ChatGPT為代表的大型語言模型(LLM)在各項任務上的高效表現(xiàn)彰顯了其
什么是CloudReady?CloudReady是一個基于谷歌的ChromiumOS開源代碼倉庫
隨著ChatGPT風靡全球,其開發(fā)商OpenAI就接連不斷地面臨著越來越多的質
一、數(shù)據(jù)中心用電量十分驚人以愛爾蘭為例,過去幾年,愛爾蘭家庭的電力
射孔概念股有哪些?7月12日相關股票股價查詢,中海油服(601808):7月1
7月11日,安徽省六安市中級人民法院對安徽財經大學黨委原常委、原副校
中國網財經7月12日訊記者張增艷新疆火炬日前公告稱公司因收購國能燃氣6
“小朋友,沒有家長帶領不能獨自游泳哦!”7月10日下午6點,縣義工協(xié)會
人工智能正在革新數(shù)據(jù)轉換工具,提高效率、準確性和實時處理。高效的數(shù)