[R+shell] 5分鐘製作linux C函數流程圖 - DiagrammeR

grViz製作過程如下:

視頻封面

03:30可視化 - linux函數流程圖

-----

推薦專欄另外兩篇相關文章: linux C函數調用棧 以及 linux文件樹

老白Walt:[R] 如何用樹展示linux C函數調用棧 - collapsibleTree

老白Walt:[R] 展示linux文件樹 - collapsibleTree


1. schedule()的流程圖 --- mermaid()製作

schedule()流程圖

R源碼如下:

library(DiagrammeR)
library(dplyr)

add_box <- function(df, t, pref, suff) {
df$desc[df$type %in% t] %<>% paste0(pref, ., suff)
return(df)
}

fc <- getcsv(d:/linux-master/schedule_flow.csv) %>%
add_box(c(d, r), (, )) %>%
add_box(c(s), [, ]) %>%
add_box(c(c), {, }) %>%
mutate(seq = paste0(id, desc))

fc$seq = sub(^(\d..+)\((.*)\), \1<br/>arg: \2, fc$seq, perl = T)

data <- select(fc, id, eid = tid) %>%
na.omit() %>%
mutate(sep = -->) %>%
bind_rows(select(fc, id, eid = fid) %>% na.omit %>% mutate(sep = -.->))

mmd <- sapply(select(data, id, eid), qdapTools::lookup, select(fc, id, seq)) %>%
as.data.frame() %>%
bind_cols(select(data, sep)) %>%
{paste(
graph TB,
with(., paste0(id, sep, eid, collapse =
)),
sep =

)}

mermaid(mmd)

mmd是字元串,它的格式是:

[節點因子][左形狀][內容][右形狀]-->[節點因子][左形狀][內容][右形狀]

形狀:

  • () - 有圓角的矩形
  • (()) - 圓形
  • [] - 矩形
  • {} - 菱形
  • >] - 左側凹三角, 右側矩形

2. driver_register()流程圖 - grVis製作

driver_register()流程圖

貼出可以直接執行的代碼(生成的代碼沒啥意思就不帖了):

grViz("
digraph {
graph [overlap = true]
edge [color = slategray, arrowhead = vee, penwidth = 5]
node [shape = oval, fontsize = 32, fontcolor = darkslategray, color = darkorange, width = 3, height = 2, penwidth = 3]
1. int ret; 2. struct device_driver *other
node [shape = rectangle, fontsize = 32, fontcolor = darkslategray, color = steelblue, width = 4, height = 2, penwidth = 3]
4. pr_err(`Driver %s was unable to register with bus_type %s because the bus was not initialized.
`,
drv->name, drv->bus->name); 5. return -EINVAL; 7. printk(KERN_WARNING `Driver %s needs updating - please use `
`bus_type methods
`, drv->name); 8. other = driver_find(drv->name, drv->bus); 10. printk(KERN_ERR `Error: Driver %s is already registered, `
`aborting...
`, drv->name); 11. return -EBUSY; 12. ret = bus_add_driver(drv); 14. return ret; 15. ret = driver_add_groups(drv, drv->groups); 17. bus_remove_driver(drv); 18. return ret; 19. kobject_uevent(&drv->p->kobj, KOBJ_ADD); 20. return ret
node [shape = diamond, fontsize = 32, fontcolor = darkslategray, color = mediumseagreen, width = 4, height = 3, penwidth = 4]
3. !drv->bus->p; 6. (drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown); 9. other; 13. ret; 16. ret
node [shape = point, fillcolor = slategray, color = gray, width = 1, height = 1]
21.
1. int ret->2. struct device_driver *other; 2. struct device_driver *other->3. !drv->bus->p; 3. !drv->bus->p->4. pr_err(`Driver %s was unable to register with bus_type %s because the bus was not initialized.
`,
drv->name, drv->bus->name)[color = forestgreen]; 4. pr_err(`Driver %s was unable to register with bus_type %s because the bus was not initialized.
`,
drv->name, drv->bus->name)->5. return -EINVAL; 5. return -EINVAL->21. ; 6. (drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)->7. printk(KERN_WARNING `Driver %s needs updating - please use `
`bus_type methods
`, drv->name)[color = forestgreen]; 7. printk(KERN_WARNING `Driver %s needs updating - please use `
`bus_type methods
`, drv->name)->8. other = driver_find(drv->name, drv->bus); 8. other = driver_find(drv->name, drv->bus)->9. other; 9. other->10. printk(KERN_ERR `Error: Driver %s is already registered, `
`aborting...
`, drv->name)[color = forestgreen]; 10. printk(KERN_ERR `Error: Driver %s is already registered, `
`aborting...
`, drv->name)->11. return -EBUSY; 11. return -EBUSY->21. ; 12. ret = bus_add_driver(drv)->13. ret; 13. ret->14. return ret[color = forestgreen]; 14. return ret->21. ; 15. ret = driver_add_groups(drv, drv->groups)->16. ret; 16. ret->17. bus_remove_driver(drv)[color = forestgreen]; 17. bus_remove_driver(drv)->18. return ret; 18. return ret->21. ; 19. kobject_uevent(&drv->p->kobj, KOBJ_ADD)->20. return ret; 20. return ret->21. ; 3. !drv->bus->p->6. (drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)[color = tomato]; 6. (drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)->8. other = driver_find(drv->name, drv->bus)[color = tomato]; 9. other->12. ret = bus_add_driver(drv)[color = tomato]; 13. ret->15. ret = driver_add_groups(drv, drv->groups)[color = tomato]; 16. ret->19. kobject_uevent(&drv->p->kobj, KOBJ_ADD)[color = tomato]
}
", width = 1900, height = 1470)

說白了就是按照一定的格式將node(節點)和edge(連線)在字元串中列出來, 有興趣深入了解的可以移步下面examples:

DiagrammeR - Documentation?

rich-iannone.github.io圖標


就如視頻所示,製作linux中任意一個C函數(遵循linux coding style)的流程圖5分鐘之內就能搞定

先理一下設計思路:

  1. 首先弄清楚DiagrammeR::grViz和DiagrammeR::mermaid的數據格式 --- 節點和連線
  2. 節點即為函數中的每行代碼 - 根據分類declaration, statement, conditional statement, return繪製不同的圖形
  3. bash下需要將函數找到並獲取函數體(function body)
  4. 過濾掉注釋和空行(其實注釋很重要應該保留, 未來再考慮添加吧)
  5. 將每個獨立語句都merge成一行(linux kernel coding style會有很多statement佔用多行)
  6. 每個statement做index以便生成語句的執行順序
  7. 手動編輯生成最終的csv文件
  8. 導入R,並生成grViz/mermaid格式字元串
  9. 繪圖

難點:

  • 如何通過函數名找到函數的具體位置? - ctags! 它會生成一個tags文件, 直接到裡面搜就OK了
  • 3 ~ 7步需要用到大量的正則來完成,時間都花這上面了

最後附上本例使用的R包:

DiagrammeR?

rich-iannone.github.io圖標rich-iannone/DiagrammeR?

github.com圖標


本專欄只生產乾貨,喜歡請關註:

數據及可視化?

zhuanlan.zhihu.com圖標
推薦閱讀:

TAG:Linux內核 | C/C | 數據可視化 |