Advertisement
Guest User

Untitled

a guest
Mar 24th, 2019
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.99 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4. "flag"
  5. "fmt"
  6. "os"
  7. "reflect"
  8. "sort"
  9. "strings"
  10.  
  11. "github.com/tendermint/tendermint/blockchain"
  12. "github.com/tendermint/tendermint/mempool"
  13. "github.com/tendermint/tendermint/state"
  14.  
  15. "github.com/tendermint/tendermint/consensus"
  16.  
  17. cfg "github.com/tendermint/tendermint/config"
  18. "github.com/tendermint/tendermint/node"
  19. )
  20.  
  21. // depNode is a node in a dependency graph.
  22. type depNode struct {
  23. rt reflect.Type
  24. name string
  25. pkg string
  26. kind reflect.Kind
  27. level int
  28. dependsOn map[string]*depNode
  29. }
  30.  
  31. var supportedStructs = map[string]reflect.Type{
  32. "blockchain.BlockchainReactor": reflect.TypeOf((*blockchain.BlockchainReactor)(nil)).Elem(),
  33. "consensus.ConsensusReactor": reflect.TypeOf((*consensus.ConsensusReactor)(nil)).Elem(),
  34. "config.Config": reflect.TypeOf((*cfg.Config)(nil)).Elem(),
  35. "mempool.Mempool": reflect.TypeOf((*mempool.Mempool)(nil)).Elem(),
  36. "mempool.MempoolReactor": reflect.TypeOf((*mempool.MempoolReactor)(nil)).Elem(),
  37. "node.Node": reflect.TypeOf((*node.Node)(nil)).Elem(),
  38. "state.BlockExecutor": reflect.TypeOf((*state.BlockExecutor)(nil)).Elem(),
  39. }
  40.  
  41. var rootCmd *flag.FlagSet
  42.  
  43. var (
  44. flagList bool
  45. flagStruct string
  46. flagDepth int
  47. flagExclude string
  48. )
  49.  
  50. func init() {
  51. rootCmd = flag.NewFlagSet("root", flag.ExitOnError)
  52. rootCmd.Usage = func() {
  53. fmt.Println(`Object graph visualization for Tendermint data structures.
  54.  
  55. Usage:
  56. tm-obj-viz [flags] <struct> <outputFile>
  57.  
  58. Flags:`)
  59. rootCmd.PrintDefaults()
  60. fmt.Println("")
  61. }
  62. rootCmd.IntVar(&flagDepth, "depth", 3, "how many levels deep to recursively analyze the object graph")
  63. rootCmd.BoolVar(&flagList, "list", false, "specify this flag to list the available structs for visualization")
  64. rootCmd.StringVar(&flagStruct, "struct", "node.Node", "the struct whose object graph is to be printed out")
  65. rootCmd.StringVar(&flagExclude, "exclude", "", "a comma-separated list of types to exclude when rendering the graph")
  66. }
  67.  
  68. func showSupportedStructs() {
  69. structs := make([]string, 0)
  70. for structName := range supportedStructs {
  71. structs = append(structs, structName)
  72. }
  73. sort.SliceStable(structs[:], func(i, j int) bool {
  74. return strings.Compare(structs[i], structs[j]) < 0
  75. })
  76. fmt.Println("Supported structs:")
  77. for _, structName := range structs {
  78. fmt.Printf(" %s\n", structName)
  79. }
  80. }
  81.  
  82. func longNodeID(t reflect.Type) string {
  83. return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
  84. }
  85.  
  86. func (n *depNode) longPath() string {
  87. return longNodeID(n.rt)
  88. }
  89.  
  90. func shortNodeID(t reflect.Type) string {
  91. return strings.Replace(t.PkgPath(), "github.com/tendermint/tendermint/", "", 1) + "." + t.Name()
  92. }
  93.  
  94. func (n *depNode) shortPath() string {
  95. return shortNodeID(n.rt)
  96. }
  97.  
  98. func (n *depNode) isTendermint() bool {
  99. tmparts := strings.Split(n.pkg, "github.com/tendermint/tendermint")
  100. return (len(tmparts) > 1) && (len(strings.Split(tmparts[1], "vendor/")) < 2)
  101. }
  102.  
  103. func (n *depNode) attr() string {
  104. attrParts := make([]string, 0)
  105. if n.isTendermint() {
  106. switch n.kind {
  107. case reflect.Struct:
  108. attrParts = append(attrParts, "style=filled", "fillcolor=lightblue")
  109. case reflect.Interface:
  110. attrParts = append(attrParts, "style=filled", "fillcolor=\"#eeeeee\"")
  111. default:
  112. attrParts = append(attrParts, "style=filled", "fillcolor=\"#ccffcc\"")
  113. }
  114. } else {
  115. attrParts = append(attrParts, "style=dashed")
  116. }
  117. return strings.Join(attrParts, ",")
  118. }
  119.  
  120. func buildDepGraph(n *depNode, maxLevels, curLevel int, seenDeps map[string]*depNode, exclude map[string]interface{}) int {
  121. if maxLevels == curLevel {
  122. return curLevel
  123. }
  124. highestLevel := curLevel
  125. if n.kind == reflect.Struct {
  126. for i := 0; i < n.rt.NumField(); i++ {
  127. subFieldType := n.rt.Field(i).Type
  128. for subFieldType.Kind() == reflect.Ptr {
  129. subFieldType = subFieldType.Elem()
  130. }
  131. if len(subFieldType.Name()) > 0 && len(subFieldType.PkgPath()) > 0 {
  132. longID := longNodeID(subFieldType)
  133. shortID := shortNodeID(subFieldType)
  134. // if we haven't explicitly excluded this particular type
  135. if _, ok := exclude[shortID]; !ok {
  136. subNode, ok := seenDeps[longID]
  137. // if we haven't seen this type before
  138. if !ok {
  139. subNode = &depNode{
  140. rt: subFieldType,
  141. name: subFieldType.Name(),
  142. pkg: subFieldType.PkgPath(),
  143. kind: subFieldType.Kind(),
  144. level: n.level + 1,
  145. dependsOn: make(map[string]*depNode),
  146. }
  147. seenDeps[longID] = subNode
  148. if subNode.kind == reflect.Struct && subNode.isTendermint() {
  149. highestLevel = buildDepGraph(subNode, maxLevels, curLevel+1, seenDeps, exclude)
  150. }
  151. }
  152. n.dependsOn[longID] = subNode
  153. }
  154. }
  155. }
  156. }
  157. return highestLevel
  158. }
  159.  
  160. // printDepNode recursively prints the given dependency node's sub-dependencies.
  161. func printDepNode(n *depNode, curLevel int, seenEdges map[string]interface{}, rootIndent string) {
  162. indent := rootIndent + strings.Repeat(" ", curLevel+1)
  163. for _, subNode := range n.dependsOn {
  164. edge := fmt.Sprintf("\"%s\"", n.shortPath()) + " -> " + fmt.Sprintf("\"%s\"", subNode.shortPath())
  165. if _, ok := seenEdges[edge]; !ok {
  166. seenEdges[edge] = nil
  167. fmt.Println(indent + edge + ";")
  168. printDepNode(subNode, curLevel+1, seenEdges, rootIndent)
  169. }
  170. }
  171. }
  172.  
  173. func printGraphFormatting(n *depNode, seenNodes map[string]*depNode) {
  174. attrs := n.attr()
  175. if len(attrs) > 0 {
  176. fmt.Println(fmt.Sprintf(" \"%s\" [%s];", n.shortPath(), n.attr()))
  177. }
  178. for id, subNode := range n.dependsOn {
  179. if _, ok := seenNodes[id]; !ok {
  180. seenNodes[id] = subNode
  181. printGraphFormatting(subNode, seenNodes)
  182. }
  183. }
  184. }
  185.  
  186. func printLegend() {
  187. fmt.Println(" subgraph cluster_legend {")
  188. fmt.Println(" fontname = \"helvetica\";")
  189. fmt.Println(" fontsize = 18;")
  190. fmt.Println(" label = \"Legend\";")
  191. fmt.Println(" color = lightgray;")
  192. fmt.Println(" \"Tendermint struct\" [style=filled,fillcolor=lightblue];")
  193. fmt.Println(" \"Tendermint interface\" [style=filled,fillcolor=\"#eeeeee\"];")
  194. fmt.Println(" \"Tendermint type\" [style=filled,fillcolor=\"#ccffcc\"];")
  195. fmt.Println(" \"Non-Tendermint type\" [style=dashed];")
  196. fmt.Println(" }")
  197. }
  198.  
  199. func printExcluded(exclude map[string]interface{}) {
  200. if len(exclude) > 0 {
  201. fmt.Println(" subgraph cluster_exclude {")
  202. fmt.Println(" fontname = \"helvetica\";")
  203. fmt.Println(" fontsize = 18;")
  204. fmt.Println(" label = \"Excluded\";")
  205. fmt.Println(" color = lightgray;")
  206. for e := range exclude {
  207. fmt.Println(" \"" + e + "\" [style=filled,fillcolor=\"#ff8888\"];")
  208. }
  209. fmt.Println(" }")
  210. }
  211. }
  212.  
  213. func printDepGraph(s reflect.Type, maxLevels int, exclude map[string]interface{}) {
  214. for s.Kind() == reflect.Ptr {
  215. s = s.Elem()
  216. }
  217. rootNode := &depNode{
  218. rt: s,
  219. name: s.Name(),
  220. pkg: s.PkgPath(),
  221. kind: s.Kind(),
  222. level: 0,
  223. dependsOn: make(map[string]*depNode),
  224. }
  225. seenDeps := make(map[string]*depNode)
  226. buildDepGraph(rootNode, maxLevels, 0, seenDeps, exclude)
  227.  
  228. fmt.Println("digraph G {")
  229. fmt.Println(" rankdir=\"LR\";")
  230. fmt.Println(" ranksep=\"2 equally\";")
  231. fmt.Println(" splines=spline;")
  232. fmt.Println(" ratio=fill;")
  233. fmt.Println(" node [ fontname=\"helvetica\" ];")
  234.  
  235. // then print all the edges
  236. seenEdges := make(map[string]interface{})
  237. fmt.Println(" subgraph cluster_graph {")
  238. fmt.Println(" style=invis;")
  239. printDepNode(rootNode, 0, seenEdges, " ")
  240. fmt.Println(" }")
  241.  
  242. // format the different kinds of nodes
  243. seenDeps = make(map[string]*depNode)
  244. printGraphFormatting(rootNode, seenDeps)
  245.  
  246. // print out which classes have been excluded from the graph
  247. printExcluded(exclude)
  248.  
  249. // print out the legend
  250. printLegend()
  251.  
  252. fmt.Println("}")
  253. }
  254.  
  255. func main() {
  256. if err := rootCmd.Parse(os.Args[1:]); err != nil {
  257. fmt.Println("Failed to parse command line arguments:", err)
  258. os.Exit(1)
  259. }
  260.  
  261. if flagList {
  262. showSupportedStructs()
  263. os.Exit(0)
  264. }
  265.  
  266. t, ok := supportedStructs[flagStruct]
  267. if !ok {
  268. fmt.Println("Unrecognized struct:", flagStruct)
  269. os.Exit(1)
  270. }
  271. exclude := make(map[string]interface{})
  272. for _, e := range strings.Split(flagExclude, ",") {
  273. if len(e) > 0 {
  274. exclude[e] = nil
  275. }
  276. }
  277. printDepGraph(t, flagDepth, exclude)
  278. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement