graph.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. // Copyright (c) 2019 Uber Technologies, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package dot
  21. import (
  22. "fmt"
  23. "reflect"
  24. )
  25. // ErrorType of a constructor or group is updated when they fail to build.
  26. type ErrorType int
  27. const (
  28. noError ErrorType = iota
  29. rootCause
  30. transitiveFailure
  31. )
  32. // CtorID is a unique numeric identifier for constructors.
  33. type CtorID uintptr
  34. // Ctor encodes a constructor provided to the container for the DOT graph.
  35. type Ctor struct {
  36. Name string
  37. Package string
  38. File string
  39. Line int
  40. ID CtorID
  41. Params []*Param
  42. GroupParams []*Group
  43. Results []*Result
  44. ErrorType ErrorType
  45. }
  46. // removeParam deletes the dependency on the provided result's nodeKey.
  47. // This is used to prune links to results of deleted constructors.
  48. func (c *Ctor) removeParam(k nodeKey) {
  49. var pruned []*Param
  50. for _, p := range c.Params {
  51. if k != p.nodeKey() {
  52. pruned = append(pruned, p)
  53. }
  54. }
  55. c.Params = pruned
  56. }
  57. type nodeKey struct {
  58. t reflect.Type
  59. name string
  60. group string
  61. }
  62. // Node is a single node in a graph and is embedded into Params and Results.
  63. type Node struct {
  64. Type reflect.Type
  65. Name string
  66. Group string
  67. }
  68. func (n *Node) nodeKey() nodeKey {
  69. return nodeKey{t: n.Type, name: n.Name, group: n.Group}
  70. }
  71. // Param is a parameter node in the graph. Parameters are the input to constructors.
  72. type Param struct {
  73. *Node
  74. Optional bool
  75. }
  76. // Result is a result node in the graph. Results are the output of constructors.
  77. type Result struct {
  78. *Node
  79. // GroupIndex is added to differentiate grouped values from one another.
  80. // Since grouped values have the same type and group, their Node / string
  81. // representations are the same so we need indices to uniquely identify
  82. // the values.
  83. GroupIndex int
  84. }
  85. // Group is a group node in the graph. Group represents an fx value group.
  86. type Group struct {
  87. // Type is the type of values in the group.
  88. Type reflect.Type
  89. Name string
  90. Results []*Result
  91. ErrorType ErrorType
  92. }
  93. func (g *Group) nodeKey() nodeKey {
  94. return nodeKey{t: g.Type, group: g.Name}
  95. }
  96. // TODO(rhang): Avoid linear search to discover group results that should be pruned.
  97. func (g *Group) removeResult(r *Result) {
  98. var pruned []*Result
  99. for _, rg := range g.Results {
  100. if r.GroupIndex != rg.GroupIndex {
  101. pruned = append(pruned, rg)
  102. }
  103. }
  104. g.Results = pruned
  105. }
  106. // Graph is the DOT-format graph in a Container.
  107. type Graph struct {
  108. Ctors []*Ctor
  109. ctorMap map[CtorID]*Ctor
  110. Groups []*Group
  111. groupMap map[nodeKey]*Group
  112. consumers map[nodeKey][]*Ctor
  113. Failed *FailedNodes
  114. }
  115. // FailedNodes is the nodes that failed in the graph.
  116. type FailedNodes struct {
  117. // RootCauses is a list of the point of failures. They are the root causes
  118. // of failed invokes and can be either missing types (not provided) or
  119. // error types (error providing).
  120. RootCauses []*Result
  121. // TransitiveFailures is the list of nodes that failed to build due to
  122. // missing/failed dependencies.
  123. TransitiveFailures []*Result
  124. // ctors is a collection of failed constructors IDs that are populated as the graph is
  125. // traversed for errors.
  126. ctors map[CtorID]struct{}
  127. // Groups is a collection of failed groupKeys that is populated as the graph is traversed
  128. // for errors.
  129. groups map[nodeKey]struct{}
  130. }
  131. // NewGraph creates an empty graph.
  132. func NewGraph() *Graph {
  133. return &Graph{
  134. ctorMap: make(map[CtorID]*Ctor),
  135. groupMap: make(map[nodeKey]*Group),
  136. consumers: make(map[nodeKey][]*Ctor),
  137. Failed: &FailedNodes{
  138. ctors: make(map[CtorID]struct{}),
  139. groups: make(map[nodeKey]struct{}),
  140. },
  141. }
  142. }
  143. // NewGroup creates a new group with information in the groupKey.
  144. func NewGroup(k nodeKey) *Group {
  145. return &Group{
  146. Type: k.t,
  147. Name: k.group,
  148. }
  149. }
  150. // AddCtor adds the constructor with paramList and resultList into the graph.
  151. func (dg *Graph) AddCtor(c *Ctor, paramList []*Param, resultList []*Result) {
  152. var (
  153. params []*Param
  154. groupParams []*Group
  155. )
  156. // Loop through the paramList to separate them into regular params and
  157. // grouped params. For grouped params, we use getGroup to find the actual
  158. // group.
  159. for _, param := range paramList {
  160. if param.Group == "" {
  161. // Not a value group.
  162. params = append(params, param)
  163. continue
  164. }
  165. k := nodeKey{t: param.Type.Elem(), group: param.Group}
  166. group := dg.getGroup(k)
  167. groupParams = append(groupParams, group)
  168. }
  169. for _, result := range resultList {
  170. // If the result is a grouped value, we want to update its GroupIndex
  171. // and add it to the Group.
  172. if result.Group != "" {
  173. dg.addToGroup(result, c.ID)
  174. }
  175. }
  176. c.Params = params
  177. c.GroupParams = groupParams
  178. c.Results = resultList
  179. // Track which constructors consume a parameter.
  180. for _, p := range paramList {
  181. k := p.nodeKey()
  182. dg.consumers[k] = append(dg.consumers[k], c)
  183. }
  184. dg.Ctors = append(dg.Ctors, c)
  185. dg.ctorMap[c.ID] = c
  186. }
  187. func (dg *Graph) failNode(r *Result, isRootCause bool) {
  188. if isRootCause {
  189. dg.addRootCause(r)
  190. } else {
  191. dg.addTransitiveFailure(r)
  192. }
  193. }
  194. // AddMissingNodes adds missing nodes to the list of failed Results in the graph.
  195. func (dg *Graph) AddMissingNodes(results []*Result) {
  196. // The failure(s) are root causes if there are no other failures.
  197. isRootCause := len(dg.Failed.RootCauses) == 0
  198. for _, r := range results {
  199. dg.failNode(r, isRootCause)
  200. }
  201. }
  202. // FailNodes adds results to the list of failed Results in the graph, and
  203. // updates the state of the constructor with the given id accordingly.
  204. func (dg *Graph) FailNodes(results []*Result, id CtorID) {
  205. // This failure is the root cause if there are no other failures.
  206. isRootCause := len(dg.Failed.RootCauses) == 0
  207. dg.Failed.ctors[id] = struct{}{}
  208. for _, r := range results {
  209. dg.failNode(r, isRootCause)
  210. }
  211. if c, ok := dg.ctorMap[id]; ok {
  212. if isRootCause {
  213. c.ErrorType = rootCause
  214. } else {
  215. c.ErrorType = transitiveFailure
  216. }
  217. }
  218. }
  219. // FailGroupNodes finds and adds the failed grouped nodes to the list of failed
  220. // Results in the graph, and updates the state of the group and constructor
  221. // with the given id accordingly.
  222. func (dg *Graph) FailGroupNodes(name string, t reflect.Type, id CtorID) {
  223. // This failure is the root cause if there are no other failures.
  224. isRootCause := len(dg.Failed.RootCauses) == 0
  225. k := nodeKey{t: t, group: name}
  226. group := dg.getGroup(k)
  227. // If the ctor does not exist it cannot be failed.
  228. if _, ok := dg.ctorMap[id]; !ok {
  229. return
  230. }
  231. // Track which constructors and groups have failed.
  232. dg.Failed.ctors[id] = struct{}{}
  233. dg.Failed.groups[k] = struct{}{}
  234. for _, r := range dg.ctorMap[id].Results {
  235. if r.Type == t && r.Group == name {
  236. dg.failNode(r, isRootCause)
  237. }
  238. }
  239. if c, ok := dg.ctorMap[id]; ok {
  240. if isRootCause {
  241. group.ErrorType = rootCause
  242. c.ErrorType = rootCause
  243. } else {
  244. group.ErrorType = transitiveFailure
  245. c.ErrorType = transitiveFailure
  246. }
  247. }
  248. }
  249. // getGroup finds the group by nodeKey from the graph. If it is not available,
  250. // a new group is created and returned.
  251. func (dg *Graph) getGroup(k nodeKey) *Group {
  252. g, ok := dg.groupMap[k]
  253. if !ok {
  254. g = NewGroup(k)
  255. dg.groupMap[k] = g
  256. dg.Groups = append(dg.Groups, g)
  257. }
  258. return g
  259. }
  260. // addToGroup adds a newly provided grouped result to the appropriate group.
  261. func (dg *Graph) addToGroup(r *Result, id CtorID) {
  262. k := nodeKey{t: r.Type, group: r.Group}
  263. group := dg.getGroup(k)
  264. r.GroupIndex = len(group.Results)
  265. group.Results = append(group.Results, r)
  266. }
  267. // PruneSuccess removes elements from the graph that do not have failed results.
  268. // Removing elements that do not have failing results makes the graph easier to debug,
  269. // since non-failing nodes and edges can clutter the graph and don't help the user debug.
  270. func (dg *Graph) PruneSuccess() {
  271. dg.pruneCtors(dg.Failed.ctors)
  272. dg.pruneGroups(dg.Failed.groups)
  273. }
  274. // pruneCtors removes constructors from the graph that do not have failing Results.
  275. func (dg *Graph) pruneCtors(failed map[CtorID]struct{}) {
  276. var pruned []*Ctor
  277. for _, c := range dg.Ctors {
  278. if _, ok := failed[c.ID]; ok {
  279. pruned = append(pruned, c)
  280. continue
  281. }
  282. // If a constructor is deleted, the constructor's stale result references need to
  283. // be removed from that result's Group and/or consuming constructor.
  284. dg.pruneCtorParams(c, dg.consumers)
  285. dg.pruneGroupResults(c, dg.groupMap)
  286. delete(dg.ctorMap, c.ID)
  287. }
  288. dg.Ctors = pruned
  289. }
  290. // pruneGroups removes groups from the graph that do not have failing results.
  291. func (dg *Graph) pruneGroups(failed map[nodeKey]struct{}) {
  292. var pruned []*Group
  293. for _, g := range dg.Groups {
  294. k := g.nodeKey()
  295. if _, ok := failed[k]; ok {
  296. pruned = append(pruned, g)
  297. continue
  298. }
  299. delete(dg.groupMap, k)
  300. }
  301. dg.Groups = pruned
  302. dg.pruneCtorGroupParams(dg.groupMap)
  303. }
  304. // pruneCtorParams removes results of the constructor argument that are still referenced in the
  305. // Params of constructors that consume those results. If the results in the constructor are found
  306. // in the params of a consuming constructor that result should be removed.
  307. func (dg *Graph) pruneCtorParams(c *Ctor, consumers map[nodeKey][]*Ctor) {
  308. for _, r := range c.Results {
  309. for _, ctor := range consumers[r.nodeKey()] {
  310. ctor.removeParam(r.nodeKey())
  311. }
  312. }
  313. }
  314. // pruneCtorGroupParams removes constructor results that are still referenced in the GroupParams of
  315. // constructors that consume those results.
  316. func (dg *Graph) pruneCtorGroupParams(groups map[nodeKey]*Group) {
  317. for _, c := range dg.Ctors {
  318. var pruned []*Group
  319. for _, gp := range c.GroupParams {
  320. k := gp.nodeKey()
  321. if _, ok := groups[k]; ok {
  322. pruned = append(pruned, gp)
  323. }
  324. }
  325. c.GroupParams = pruned
  326. }
  327. }
  328. // pruneGroupResults removes results of the constructor argument that are still referenced in
  329. // the Group object that contains that result. If a group no longer exists references to that
  330. // should should be removed.
  331. func (dg *Graph) pruneGroupResults(c *Ctor, groups map[nodeKey]*Group) {
  332. for _, r := range c.Results {
  333. k := r.nodeKey()
  334. if k.group == "" {
  335. continue
  336. }
  337. g, ok := groups[k]
  338. if ok {
  339. g.removeResult(r)
  340. }
  341. }
  342. }
  343. // String implements fmt.Stringer for Param.
  344. func (p *Param) String() string {
  345. if p.Name != "" {
  346. return fmt.Sprintf("%v[name=%v]", p.Type.String(), p.Name)
  347. }
  348. return p.Type.String()
  349. }
  350. // String implements fmt.Stringer for Result.
  351. func (r *Result) String() string {
  352. switch {
  353. case r.Name != "":
  354. return fmt.Sprintf("%v[name=%v]", r.Type.String(), r.Name)
  355. case r.Group != "":
  356. return fmt.Sprintf("%v[group=%v]%v", r.Type.String(), r.Group, r.GroupIndex)
  357. default:
  358. return r.Type.String()
  359. }
  360. }
  361. // String implements fmt.Stringer for Group.
  362. func (g *Group) String() string {
  363. return fmt.Sprintf("[type=%v group=%v]", g.Type.String(), g.Name)
  364. }
  365. // Attributes composes and returns a string of the Result node's attributes.
  366. func (r *Result) Attributes() string {
  367. switch {
  368. case r.Name != "":
  369. return fmt.Sprintf(`label=<%v<BR /><FONT POINT-SIZE="10">Name: %v</FONT>>`, r.Type, r.Name)
  370. case r.Group != "":
  371. return fmt.Sprintf(`label=<%v<BR /><FONT POINT-SIZE="10">Group: %v</FONT>>`, r.Type, r.Group)
  372. default:
  373. return fmt.Sprintf(`label=<%v>`, r.Type)
  374. }
  375. }
  376. // Attributes composes and returns a string of the Group node's attributes.
  377. func (g *Group) Attributes() string {
  378. attr := fmt.Sprintf(`shape=diamond label=<%v<BR /><FONT POINT-SIZE="10">Group: %v</FONT>>`, g.Type, g.Name)
  379. if g.ErrorType != noError {
  380. attr += " color=" + g.ErrorType.Color()
  381. }
  382. return attr
  383. }
  384. // Color returns the color representation of each ErrorType.
  385. func (s ErrorType) Color() string {
  386. switch s {
  387. case rootCause:
  388. return "red"
  389. case transitiveFailure:
  390. return "orange"
  391. default:
  392. return "black"
  393. }
  394. }
  395. func (dg *Graph) addRootCause(r *Result) {
  396. dg.Failed.RootCauses = append(dg.Failed.RootCauses, r)
  397. }
  398. func (dg *Graph) addTransitiveFailure(r *Result) {
  399. dg.Failed.TransitiveFailures = append(dg.Failed.TransitiveFailures, r)
  400. }