slab.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. # SPDX-License-Identifier: GPL-2.0
  2. #
  3. # Copyright (c) 2023 MediaTek Inc.
  4. #
  5. # Authors:
  6. # Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
  7. #
  8. import gdb
  9. import re
  10. import traceback
  11. from linux import lists, utils, stackdepot, constants, mm
  12. SLAB_RED_ZONE = constants.LX_SLAB_RED_ZONE
  13. SLAB_POISON = constants.LX_SLAB_POISON
  14. SLAB_KMALLOC = constants.LX_SLAB_KMALLOC
  15. SLAB_HWCACHE_ALIGN = constants.LX_SLAB_HWCACHE_ALIGN
  16. SLAB_CACHE_DMA = constants.LX_SLAB_CACHE_DMA
  17. SLAB_CACHE_DMA32 = constants.LX_SLAB_CACHE_DMA32
  18. SLAB_STORE_USER = constants.LX_SLAB_STORE_USER
  19. SLAB_PANIC = constants.LX_SLAB_PANIC
  20. OO_SHIFT = 16
  21. OO_MASK = (1 << OO_SHIFT) - 1
  22. if constants.LX_CONFIG_SLUB_DEBUG:
  23. slab_type = utils.CachedType("struct slab")
  24. slab_ptr_type = slab_type.get_type().pointer()
  25. kmem_cache_type = utils.CachedType("struct kmem_cache")
  26. kmem_cache_ptr_type = kmem_cache_type.get_type().pointer()
  27. freeptr_t = utils.CachedType("freeptr_t")
  28. freeptr_t_ptr = freeptr_t.get_type().pointer()
  29. track_type = gdb.lookup_type('struct track')
  30. track_alloc = int(gdb.parse_and_eval('TRACK_ALLOC'))
  31. track_free = int(gdb.parse_and_eval('TRACK_FREE'))
  32. def slab_folio(slab):
  33. return slab.cast(gdb.lookup_type("struct folio").pointer())
  34. def slab_address(slab):
  35. p_ops = mm.page_ops().ops
  36. folio = slab_folio(slab)
  37. return p_ops.folio_address(folio)
  38. def for_each_object(cache, addr, slab_objects):
  39. p = addr
  40. if cache['flags'] & SLAB_RED_ZONE:
  41. p += int(cache['red_left_pad'])
  42. while p < addr + (slab_objects * cache['size']):
  43. yield p
  44. p = p + int(cache['size'])
  45. def get_info_end(cache):
  46. if (cache['offset'] >= cache['inuse']):
  47. return cache['inuse'] + gdb.lookup_type("void").pointer().sizeof
  48. else:
  49. return cache['inuse']
  50. def get_orig_size(cache, obj):
  51. if cache['flags'] & SLAB_STORE_USER and cache['flags'] & SLAB_KMALLOC:
  52. p = mm.page_ops().ops.kasan_reset_tag(obj)
  53. p += get_info_end(cache)
  54. p += gdb.lookup_type('struct track').sizeof * 2
  55. p = p.cast(utils.get_uint_type().pointer())
  56. return p.dereference()
  57. else:
  58. return cache['object_size']
  59. def get_track(cache, object_pointer, alloc):
  60. p = object_pointer + get_info_end(cache)
  61. p += (alloc * track_type.sizeof)
  62. return p
  63. def oo_objects(x):
  64. return int(x['x']) & OO_MASK
  65. def oo_order(x):
  66. return int(x['x']) >> OO_SHIFT
  67. def reciprocal_divide(a, R):
  68. t = (a * int(R['m'])) >> 32
  69. return (t + ((a - t) >> int(R['sh1']))) >> int(R['sh2'])
  70. def __obj_to_index(cache, addr, obj):
  71. return reciprocal_divide(int(mm.page_ops().ops.kasan_reset_tag(obj)) - addr, cache['reciprocal_size'])
  72. def swab64(x):
  73. result = (((x & 0x00000000000000ff) << 56) | \
  74. ((x & 0x000000000000ff00) << 40) | \
  75. ((x & 0x0000000000ff0000) << 24) | \
  76. ((x & 0x00000000ff000000) << 8) | \
  77. ((x & 0x000000ff00000000) >> 8) | \
  78. ((x & 0x0000ff0000000000) >> 24) | \
  79. ((x & 0x00ff000000000000) >> 40) | \
  80. ((x & 0xff00000000000000) >> 56))
  81. return result
  82. def freelist_ptr_decode(cache, ptr, ptr_addr):
  83. if constants.LX_CONFIG_SLAB_FREELIST_HARDENED:
  84. return ptr['v'] ^ cache['random'] ^ swab64(int(ptr_addr))
  85. else:
  86. return ptr['v']
  87. def get_freepointer(cache, obj):
  88. obj = mm.page_ops().ops.kasan_reset_tag(obj)
  89. ptr_addr = obj + cache['offset']
  90. p = ptr_addr.cast(freeptr_t_ptr).dereference()
  91. return freelist_ptr_decode(cache, p, ptr_addr)
  92. def loc_exist(loc_track, addr, handle, waste):
  93. for loc in loc_track:
  94. if loc['addr'] == addr and loc['handle'] == handle and loc['waste'] == waste:
  95. return loc
  96. return None
  97. def add_location(loc_track, cache, track, orig_size):
  98. jiffies = gdb.parse_and_eval("jiffies_64")
  99. age = jiffies - track['when']
  100. handle = 0
  101. waste = cache['object_size'] - int(orig_size)
  102. pid = int(track['pid'])
  103. cpuid = int(track['cpu'])
  104. addr = track['addr']
  105. if constants.LX_CONFIG_STACKDEPOT:
  106. handle = track['handle']
  107. loc = loc_exist(loc_track, addr, handle, waste)
  108. if loc:
  109. loc['count'] += 1
  110. if track['when']:
  111. loc['sum_time'] += age
  112. loc['min_time'] = min(loc['min_time'], age)
  113. loc['max_time'] = max(loc['max_time'], age)
  114. loc['min_pid'] = min(loc['min_pid'], pid)
  115. loc['max_pid'] = max(loc['max_pid'], pid)
  116. loc['cpus'].add(cpuid)
  117. else:
  118. loc_track.append({
  119. 'count' : 1,
  120. 'addr' : addr,
  121. 'sum_time' : age,
  122. 'min_time' : age,
  123. 'max_time' : age,
  124. 'min_pid' : pid,
  125. 'max_pid' : pid,
  126. 'handle' : handle,
  127. 'waste' : waste,
  128. 'cpus' : {cpuid}
  129. }
  130. )
  131. def slabtrace(alloc, cache_name):
  132. def __fill_map(obj_map, cache, slab):
  133. p = slab['freelist']
  134. addr = slab_address(slab)
  135. while p != gdb.Value(0):
  136. index = __obj_to_index(cache, addr, p)
  137. obj_map[index] = True # free objects
  138. p = get_freepointer(cache, p)
  139. # process every slab page on the slab_list (partial and full list)
  140. def process_slab(loc_track, slab_list, alloc, cache):
  141. for slab in lists.list_for_each_entry(slab_list, slab_ptr_type, "slab_list"):
  142. obj_map[:] = [False] * oo_objects(cache['oo'])
  143. __fill_map(obj_map, cache, slab)
  144. addr = slab_address(slab)
  145. for object_pointer in for_each_object(cache, addr, slab['objects']):
  146. if obj_map[__obj_to_index(cache, addr, object_pointer)] == True:
  147. continue
  148. p = get_track(cache, object_pointer, alloc)
  149. track = gdb.Value(p).cast(track_type.pointer())
  150. if alloc == track_alloc:
  151. size = get_orig_size(cache, object_pointer)
  152. else:
  153. size = cache['object_size']
  154. add_location(loc_track, cache, track, size)
  155. continue
  156. slab_caches = gdb.parse_and_eval("slab_caches")
  157. if mm.page_ops().ops.MAX_NUMNODES > 1:
  158. nr_node_ids = int(gdb.parse_and_eval("nr_node_ids"))
  159. else:
  160. nr_node_ids = 1
  161. target_cache = None
  162. loc_track = []
  163. for cache in lists.list_for_each_entry(slab_caches, kmem_cache_ptr_type, 'list'):
  164. if cache['name'].string() == cache_name:
  165. target_cache = cache
  166. break
  167. obj_map = [False] * oo_objects(target_cache['oo'])
  168. if target_cache['flags'] & SLAB_STORE_USER:
  169. for i in range(0, nr_node_ids):
  170. cache_node = target_cache['node'][i]
  171. if cache_node['nr_slabs']['counter'] == 0:
  172. continue
  173. process_slab(loc_track, cache_node['partial'], alloc, target_cache)
  174. process_slab(loc_track, cache_node['full'], alloc, target_cache)
  175. else:
  176. raise gdb.GdbError("SLAB_STORE_USER is not set in %s" % target_cache['name'].string())
  177. for loc in sorted(loc_track, key=lambda x:x['count'], reverse=True):
  178. if loc['addr']:
  179. addr = loc['addr'].cast(utils.get_ulong_type().pointer())
  180. gdb.write("%d %s" % (loc['count'], str(addr).split(' ')[-1]))
  181. else:
  182. gdb.write("%d <not-available>" % loc['count'])
  183. if loc['waste']:
  184. gdb.write(" waste=%d/%d" % (loc['count'] * loc['waste'], loc['waste']))
  185. if loc['sum_time'] != loc['min_time']:
  186. gdb.write(" age=%d/%d/%d" % (loc['min_time'], loc['sum_time']/loc['count'], loc['max_time']))
  187. else:
  188. gdb.write(" age=%d" % loc['min_time'])
  189. if loc['min_pid'] != loc['max_pid']:
  190. gdb.write(" pid=%d-%d" % (loc['min_pid'], loc['max_pid']))
  191. else:
  192. gdb.write(" pid=%d" % loc['min_pid'])
  193. if constants.LX_NR_CPUS > 1:
  194. nr_cpu = gdb.parse_and_eval('__num_online_cpus')['counter']
  195. if nr_cpu > 1:
  196. gdb.write(" cpus=")
  197. gdb.write(','.join(str(cpu) for cpu in loc['cpus']))
  198. gdb.write("\n")
  199. if constants.LX_CONFIG_STACKDEPOT:
  200. if loc['handle']:
  201. stackdepot.stack_depot_print(loc['handle'])
  202. gdb.write("\n")
  203. def help():
  204. t = """Usage: lx-slabtrace --cache_name [cache_name] [Options]
  205. Options:
  206. --alloc
  207. print information of allocation trace of the allocated objects
  208. --free
  209. print information of freeing trace of the allocated objects
  210. Example:
  211. lx-slabtrace --cache_name kmalloc-1k --alloc
  212. lx-slabtrace --cache_name kmalloc-1k --free\n"""
  213. gdb.write("Unrecognized command\n")
  214. raise gdb.GdbError(t)
  215. class LxSlabTrace(gdb.Command):
  216. """Show specific cache slabtrace"""
  217. def __init__(self):
  218. super(LxSlabTrace, self).__init__("lx-slabtrace", gdb.COMMAND_DATA)
  219. def invoke(self, arg, from_tty):
  220. if not constants.LX_CONFIG_SLUB_DEBUG:
  221. raise gdb.GdbError("CONFIG_SLUB_DEBUG is not enabled")
  222. argv = gdb.string_to_argv(arg)
  223. alloc = track_alloc # default show alloc_traces
  224. if len(argv) == 3:
  225. if argv[2] == '--alloc':
  226. alloc = track_alloc
  227. elif argv[2] == '--free':
  228. alloc = track_free
  229. else:
  230. help()
  231. if len(argv) >= 2 and argv[0] == '--cache_name':
  232. slabtrace(alloc, argv[1])
  233. else:
  234. help()
  235. LxSlabTrace()
  236. def slabinfo():
  237. nr_node_ids = None
  238. if not constants.LX_CONFIG_SLUB_DEBUG:
  239. raise gdb.GdbError("CONFIG_SLUB_DEBUG is not enabled")
  240. def count_free(slab):
  241. total_free = 0
  242. for slab in lists.list_for_each_entry(slab, slab_ptr_type, 'slab_list'):
  243. total_free += int(slab['objects'] - slab['inuse'])
  244. return total_free
  245. gdb.write("{:^18} | {:^20} | {:^12} | {:^12} | {:^8} | {:^11} | {:^13}\n".format('Pointer', 'name', 'active_objs', 'num_objs', 'objsize', 'objperslab', 'pagesperslab'))
  246. gdb.write("{:-^18} | {:-^20} | {:-^12} | {:-^12} | {:-^8} | {:-^11} | {:-^13}\n".format('', '', '', '', '', '', ''))
  247. slab_caches = gdb.parse_and_eval("slab_caches")
  248. if mm.page_ops().ops.MAX_NUMNODES > 1:
  249. nr_node_ids = int(gdb.parse_and_eval("nr_node_ids"))
  250. else:
  251. nr_node_ids = 1
  252. for cache in lists.list_for_each_entry(slab_caches, kmem_cache_ptr_type, 'list'):
  253. nr_objs = 0
  254. nr_free = 0
  255. nr_slabs = 0
  256. for i in range(0, nr_node_ids):
  257. cache_node = cache['node'][i]
  258. try:
  259. nr_slabs += cache_node['nr_slabs']['counter']
  260. nr_objs = int(cache_node['total_objects']['counter'])
  261. nr_free = count_free(cache_node['partial'])
  262. except:
  263. raise gdb.GdbError(traceback.format_exc())
  264. active_objs = nr_objs - nr_free
  265. num_objs = nr_objs
  266. active_slabs = nr_slabs
  267. objects_per_slab = oo_objects(cache['oo'])
  268. cache_order = oo_order(cache['oo'])
  269. gdb.write("{:18s} | {:20.19s} | {:12} | {:12} | {:8} | {:11} | {:13}\n".format(hex(cache), cache['name'].string(), str(active_objs), str(num_objs), str(cache['size']), str(objects_per_slab), str(1 << cache_order)))
  270. class LxSlabInfo(gdb.Command):
  271. """Show slabinfo"""
  272. def __init__(self):
  273. super(LxSlabInfo, self).__init__("lx-slabinfo", gdb.COMMAND_DATA)
  274. def invoke(self, arg, from_tty):
  275. slabinfo()
  276. LxSlabInfo()