sizeof.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. // Copyright (c) 2017 Arista Networks, Inc. All rights reserved.
  2. // Arista Networks, Inc. Confidential and Proprietary.
  3. // Subject to Arista Networks, Inc.'s EULA.
  4. // FOR INTERNAL USE ONLY. NOT FOR DISTRIBUTION.
  5. package sizeof
  6. import (
  7. "errors"
  8. "reflect"
  9. "unsafe"
  10. "notabug.org/themusicgod1/goarista/areflect"
  11. )
  12. // blocks are used to keep track of which memory areas were already
  13. // been visited.
  14. type block struct {
  15. start uintptr
  16. end uintptr
  17. }
  18. func (b block) size() uintptr {
  19. return b.end - b.start
  20. }
  21. // DeepSizeof returns total memory occupied by each type for val.
  22. // The value passed in argument must be a pointer.
  23. func DeepSizeof(val interface{}) (map[string]uintptr, error) {
  24. value := reflect.ValueOf(val)
  25. // We want to force val to be a pointer to the original value, because if we get a copy, we
  26. // can get some pointers that will point back to our original value.
  27. if value.Kind() != reflect.Ptr {
  28. return nil, errors.New("cannot get the deep size of a non-pointer value")
  29. }
  30. m := make(map[string]uintptr)
  31. ptrsTypes := make(map[uintptr]map[string]struct{})
  32. sizeof(value.Elem(), m, ptrsTypes, false, block{start: uintptr(value.Pointer())}, nil)
  33. return m, nil
  34. }
  35. // Check if curBlock overlap tmpBlock
  36. func isOverlapping(curBlock, tmpBlock block) bool {
  37. return curBlock.start <= tmpBlock.end && tmpBlock.start <= curBlock.end
  38. }
  39. func getOverlappingBlocks(curBlock block, seen []block) ([]block, int) {
  40. var tmp []block
  41. for idx, a := range seen {
  42. if a.start > curBlock.end {
  43. return tmp, idx
  44. }
  45. if isOverlapping(curBlock, a) {
  46. tmp = append(tmp, a)
  47. }
  48. }
  49. return tmp, len(seen)
  50. }
  51. func insertBlock(curBlock block, idxToInsert int, seen []block) []block {
  52. seen = append(seen, block{})
  53. copy(seen[idxToInsert+1:], seen[idxToInsert:])
  54. seen[idxToInsert] = curBlock
  55. return seen
  56. }
  57. // get the size of our current block that is not overlapping other blocks.
  58. func getUnseenSizeOfCurrentBlock(curBlock block, overlappingBlocks []block) uintptr {
  59. var size uintptr
  60. for idx, a := range overlappingBlocks {
  61. if idx == 0 && curBlock.start < a.start {
  62. size += a.start - curBlock.start
  63. }
  64. if idx == len(overlappingBlocks)-1 {
  65. if curBlock.end > a.end {
  66. size += curBlock.end - a.end
  67. }
  68. } else {
  69. size += overlappingBlocks[idx+1].start - a.end
  70. }
  71. }
  72. return size
  73. }
  74. func updateSeenBlocks(curBlock block, seen []block) ([]block, uintptr) {
  75. if len(seen) == 0 {
  76. return []block{curBlock}, curBlock.size()
  77. }
  78. overlappingBlocks, idx := getOverlappingBlocks(curBlock, seen)
  79. if len(overlappingBlocks) == 0 {
  80. // No overlap, so we will insert our new block in our list.
  81. return insertBlock(curBlock, idx, seen), curBlock.size()
  82. }
  83. unseenSize := getUnseenSizeOfCurrentBlock(curBlock, overlappingBlocks)
  84. idxFirstOverlappingBlock := idx - len(overlappingBlocks)
  85. firstOverlappingBlock := &seen[idxFirstOverlappingBlock]
  86. lastOverlappingBlock := seen[idx-1]
  87. if firstOverlappingBlock.start > curBlock.start {
  88. firstOverlappingBlock.start = curBlock.start
  89. }
  90. if lastOverlappingBlock.end < curBlock.end {
  91. firstOverlappingBlock.end = curBlock.end
  92. } else {
  93. firstOverlappingBlock.end = lastOverlappingBlock.end
  94. }
  95. tailLen := len(seen[idx:])
  96. copy(seen[idxFirstOverlappingBlock+1:], seen[idx:])
  97. return seen[:idxFirstOverlappingBlock+1+tailLen], unseenSize
  98. }
  99. // Check if this current block is already fully contained in our list of seen blocks
  100. func isKnownBlock(curBlock block, seen []block) bool {
  101. for _, a := range seen {
  102. if a.start <= curBlock.start &&
  103. a.end >= curBlock.end {
  104. // curBlock is fully contained in an other block
  105. // that we already know
  106. return true
  107. }
  108. if a.start > curBlock.start {
  109. // Our slice of seens block is order by pointer address.
  110. // That means, if curBlock was not contained in a previous known
  111. // block, there is no need to continue.
  112. return false
  113. }
  114. }
  115. return false
  116. }
  117. func sizeof(v reflect.Value, m map[string]uintptr, ptrsTypes map[uintptr]map[string]struct{},
  118. counted bool, curBlock block, seen []block) []block {
  119. if !v.IsValid() {
  120. return seen
  121. }
  122. vn := v.Type().String()
  123. vs := v.Type().Size()
  124. curBlock.end = vs + curBlock.start
  125. if counted {
  126. // already accounted for the size (field in struct, in array, etc)
  127. vs = 0
  128. }
  129. if curBlock.start != 0 {
  130. // A pointer can point to the same memory area than a previous pointer,
  131. // but its type should be different (see tests struct_5 and struct_6).
  132. if types, ok := ptrsTypes[curBlock.start]; ok {
  133. if _, ok := types[vn]; ok {
  134. return seen
  135. }
  136. types[vn] = struct{}{}
  137. } else {
  138. ptrsTypes[curBlock.start] = make(map[string]struct{})
  139. }
  140. if isKnownBlock(curBlock, seen) {
  141. // we don't want to count this size if we have a known block
  142. vs = 0
  143. } else {
  144. var tmpVs uintptr
  145. seen, tmpVs = updateSeenBlocks(curBlock, seen)
  146. if !counted {
  147. vs = tmpVs
  148. }
  149. }
  150. }
  151. switch v.Kind() {
  152. case reflect.Interface:
  153. seen = sizeof(v.Elem(), m, ptrsTypes, false, block{}, seen)
  154. case reflect.Ptr:
  155. if v.IsNil() {
  156. break
  157. }
  158. seen = sizeof(v.Elem(), m, ptrsTypes, false, block{start: uintptr(v.Pointer())}, seen)
  159. case reflect.Array:
  160. // get size of all elements in the array in case there are pointers
  161. l := v.Len()
  162. for i := 0; i < l; i++ {
  163. seen = sizeof(v.Index(i), m, ptrsTypes, true, block{}, seen)
  164. }
  165. case reflect.Slice:
  166. // get size of all elements in the slice in case there are pointers
  167. // TODO: count elements that are not accessible after reslicing
  168. l := v.Len()
  169. vLen := v.Type().Elem().Size()
  170. for i := 0; i < l; i++ {
  171. e := v.Index(i)
  172. eStart := uintptr(e.UnsafeAddr())
  173. eBlock := block{
  174. start: eStart,
  175. end: eStart + vLen,
  176. }
  177. if !isKnownBlock(eBlock, seen) {
  178. vs += vLen
  179. seen = sizeof(e, m, ptrsTypes, true, eBlock, seen)
  180. }
  181. }
  182. capStart := uintptr(v.Pointer()) + (v.Type().Elem().Size() * uintptr(v.Len()))
  183. capEnd := uintptr(v.Pointer()) + (v.Type().Elem().Size() * uintptr(v.Cap()))
  184. capBlock := block{start: capStart, end: capEnd}
  185. if isKnownBlock(capBlock, seen) {
  186. break
  187. }
  188. var tmpSize uintptr
  189. seen, tmpSize = updateSeenBlocks(capBlock, seen)
  190. vs += tmpSize
  191. case reflect.Map:
  192. if v.IsNil() {
  193. break
  194. }
  195. var tmpSize uintptr
  196. if tmpSize, seen = sizeofmap(v, seen); tmpSize == 0 {
  197. // we saw this map
  198. break
  199. }
  200. vs += tmpSize
  201. for _, k := range v.MapKeys() {
  202. kv := v.MapIndex(k)
  203. seen = sizeof(k, m, ptrsTypes, true, block{}, seen)
  204. seen = sizeof(kv, m, ptrsTypes, true, block{}, seen)
  205. }
  206. case reflect.Struct:
  207. for i, n := 0, v.NumField(); i < n; i++ {
  208. vf := areflect.ForceExport(v.Field(i))
  209. seen = sizeof(vf, m, ptrsTypes, true, block{}, seen)
  210. }
  211. case reflect.String:
  212. str := v.String()
  213. strHdr := (*reflect.StringHeader)(unsafe.Pointer(&str))
  214. tmpSize := uintptr(strHdr.Len)
  215. strBlock := block{start: strHdr.Data, end: strHdr.Data + tmpSize}
  216. if isKnownBlock(strBlock, seen) {
  217. break
  218. }
  219. seen, tmpSize = updateSeenBlocks(strBlock, seen)
  220. vs += tmpSize
  221. case reflect.Chan:
  222. var tmpSize uintptr
  223. tmpSize, seen = sizeofChan(v, m, ptrsTypes, seen)
  224. vs += tmpSize
  225. }
  226. if vs != 0 {
  227. m[vn] += vs
  228. }
  229. return seen
  230. }
  231. //go:linkname typesByString reflect.typesByString
  232. func typesByString(s string) []unsafe.Pointer
  233. func sizeofmap(v reflect.Value, seen []block) (uintptr, []block) {
  234. // get field typ *rtype of our Value v and store it in an interface
  235. var ti interface{} = v.Type()
  236. tp := (*unsafe.Pointer)(unsafe.Pointer(&ti))
  237. // we know that this pointer rtype point at the begining of struct
  238. // mapType defined in /go/src/reflect/type.go, so we can change the underlying
  239. // type of the interface to be a pointer to runtime.maptype because it as the
  240. // exact same definition as reflect.mapType.
  241. *tp = typesByString("*runtime.maptype")[0]
  242. maptypev := reflect.ValueOf(ti)
  243. maptypev = reflect.Indirect(maptypev)
  244. // now we can access field bucketsize in struct maptype
  245. bucketsize := maptypev.FieldByName("bucketsize").Uint()
  246. // get hmap
  247. var m interface{} = v.Interface()
  248. hmap := (*unsafe.Pointer)(unsafe.Pointer(&m))
  249. *hmap = typesByString("*runtime.hmap")[0]
  250. hmapv := reflect.ValueOf(m)
  251. // account for the size of the hmap, buckets and oldbuckets
  252. hmapv = reflect.Indirect(hmapv)
  253. mapBlock := block{
  254. start: hmapv.UnsafeAddr(),
  255. end: hmapv.UnsafeAddr() + hmapv.Type().Size(),
  256. }
  257. // is it a map we already saw?
  258. if isKnownBlock(mapBlock, seen) {
  259. return 0, seen
  260. }
  261. seen, _ = updateSeenBlocks(mapBlock, seen)
  262. B := hmapv.FieldByName("B").Uint()
  263. oldbuckets := hmapv.FieldByName("oldbuckets").Pointer()
  264. buckets := hmapv.FieldByName("buckets").Pointer()
  265. noverflow := int16(hmapv.FieldByName("noverflow").Uint())
  266. nb := 2
  267. if B == 0 {
  268. nb = 1
  269. }
  270. size := uint64((nb << B)) * bucketsize
  271. if noverflow != 0 {
  272. size += uint64(noverflow) * bucketsize
  273. }
  274. seen, _ = updateSeenBlocks(block{start: buckets, end: buckets + uintptr(size)},
  275. seen)
  276. // As defined in /go/src/runtime/hashmap.go in struct hmap, oldbuckets is the
  277. // previous bucket array that is half the size of the current one. We need to
  278. // also take that in consideration since there is still a pointer to this previous bucket.
  279. if oldbuckets != 0 {
  280. tmp := (2 << (B - 1)) * bucketsize
  281. size += tmp
  282. seen, _ = updateSeenBlocks(block{
  283. start: oldbuckets,
  284. end: oldbuckets + uintptr(tmp),
  285. }, seen)
  286. }
  287. return hmapv.Type().Size() + uintptr(size), seen
  288. }
  289. func getSliceToChanBuffer(buff unsafe.Pointer, buffLen uint, dataSize uint) []byte {
  290. var slice []byte
  291. sliceHdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
  292. sliceHdr.Len = int(buffLen * dataSize)
  293. sliceHdr.Cap = sliceHdr.Len
  294. sliceHdr.Data = uintptr(buff)
  295. return slice
  296. }
  297. func sizeofChan(v reflect.Value, m map[string]uintptr, ptrsTypes map[uintptr]map[string]struct{},
  298. seen []block) (uintptr, []block) {
  299. var c interface{} = v.Interface()
  300. hchan := (*unsafe.Pointer)(unsafe.Pointer(&c))
  301. *hchan = typesByString("*runtime.hchan")[0]
  302. hchanv := reflect.ValueOf(c)
  303. hchanv = reflect.Indirect(hchanv)
  304. chanBlock := block{
  305. start: hchanv.UnsafeAddr(),
  306. end: hchanv.UnsafeAddr() + hchanv.Type().Size(),
  307. }
  308. // is it a chan we already saw?
  309. if isKnownBlock(chanBlock, seen) {
  310. return 0, seen
  311. }
  312. seen, _ = updateSeenBlocks(chanBlock, seen)
  313. elemType := unsafe.Pointer(hchanv.FieldByName("elemtype").Pointer())
  314. buff := unsafe.Pointer(hchanv.FieldByName("buf").Pointer())
  315. buffLen := hchanv.FieldByName("dataqsiz").Uint()
  316. elemSize := uint16(hchanv.FieldByName("elemsize").Uint())
  317. seen, _ = updateSeenBlocks(block{
  318. start: uintptr(buff),
  319. end: uintptr(buff) + uintptr(buffLen*uint64(elemSize)),
  320. }, seen)
  321. buffSlice := getSliceToChanBuffer(buff, uint(buffLen), uint(elemSize))
  322. recvx := hchanv.FieldByName("recvx").Uint()
  323. qcount := hchanv.FieldByName("qcount").Uint()
  324. var tmp interface{}
  325. eface := (*struct {
  326. typ unsafe.Pointer
  327. ptr unsafe.Pointer
  328. })(unsafe.Pointer(&tmp))
  329. eface.typ = elemType
  330. for i := uint64(0); buffLen > 0 && i < qcount; i++ {
  331. idx := (recvx + i) % buffLen
  332. // get the pointer to the data inside the chan buffer.
  333. elem := unsafe.Pointer(&buffSlice[uint64(elemSize)*idx])
  334. eface.ptr = elem
  335. ev := reflect.ValueOf(tmp)
  336. var blk block
  337. k := ev.Kind()
  338. if k == reflect.Ptr || k == reflect.Chan || k == reflect.Map || k == reflect.Func {
  339. // let's say our chan is a chan *whatEver, or chan chan whatEver or
  340. // chan map[whatEver]whatEver. In this case elemType will
  341. // be either of type *whatEver, chan whatEver or map[whatEver]whatEver
  342. // but what we set eface.ptr = elem above, we make it point to a pointer
  343. // to where the data is sotred in the buffer of our channel.
  344. // So the interface tmp would look like:
  345. // chan *whatEver -> (type=*whatEver, ptr=**whatEver)
  346. // chan chan whatEver -> (type= chan whatEver, ptr=*chan whatEver)
  347. // chan map[whatEver]whatEver -> (type= map[whatEver]whatEver{},
  348. // ptr=*map[whatEver]whatEver)
  349. // So we need to take the ptr which is stored into the buffer and replace
  350. // the ptr to the data of our interface tmp.
  351. ptr := (*unsafe.Pointer)(elem)
  352. eface.ptr = *ptr
  353. ev = reflect.ValueOf(tmp)
  354. ev = reflect.Indirect(ev)
  355. blk.start = uintptr(*ptr)
  356. }
  357. // It seems that when the chan is of type chan *whatEver, the type in eface
  358. // will be whatEver and not *whatEver, but ev.Kind() will be a reflect.ptr.
  359. // So if k is a reflect.Ptr (i.e. a pointer) to a struct, then we want to take
  360. // the size of the struct into account because
  361. // vs := v.Type().Size() will return us the size of the struct and not the size
  362. // of the pointer that is in the channel's buffer.
  363. seen = sizeof(ev, m, ptrsTypes, true && k != reflect.Ptr, blk, seen)
  364. }
  365. return hchanv.Type().Size() + uintptr(uint64(elemSize)*buffLen), seen
  366. }