applib_malloc.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. # Copyright 2024 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import argparse
  15. import json
  16. import os
  17. import sh
  18. import string
  19. class ApplibType(object):
  20. def __init__(self, type_dict):
  21. self.name = type_dict['name']
  22. self.check_size = 1 # C preproc bool: 1 = true, 0 = false
  23. self.min_sdk = 0
  24. self.size_2x = type_dict.get('size_2x', 0)
  25. self.size_3x_direct_padding = type_dict.get('size_3x_padding', 0)
  26. self.size_3x = type_dict.get('size_3x', 0)
  27. self.dependencies = type_dict.get('dependencies', [])
  28. self.total_3x_padding = None
  29. def get_total_3x_padding(self, all_types):
  30. """ Return the amount of padding to use for the 3x version of the struct including both
  31. the direct padding we add for this struct in particular as well as all padding needed
  32. for all dependenant structs.
  33. """
  34. if self.total_3x_padding is not None:
  35. # We have it cached, just return the previously calculated value
  36. return self.total_3x_padding
  37. self.total_3x_padding = self.size_3x_direct_padding
  38. for d in self.dependencies:
  39. parent = next(filter(lambda t: d == t.name, all_types))
  40. self.total_3x_padding += parent.get_total_3x_padding(all_types)
  41. return self.total_3x_padding
  42. def __repr__(self):
  43. return '<%s %s>' % (self.__class__.__name__, self.name)
  44. def get_types(data):
  45. return [ApplibType(t) for t in data['types'] if 'name' in t]
  46. def writeline(f, str=''):
  47. f.write(str + '\n')
  48. def write_template(f, filepath, replace):
  49. with open(filepath, 'r') as template_file:
  50. template = string.Template(template_file.read())
  51. f.write(template.safe_substitute(**replace) + '\n')
  52. def generate_header(data, output_filename):
  53. all_types = get_types(data)
  54. with open(output_filename, 'w') as f:
  55. write_template(f, 'tools/applib_malloc.template.h', {
  56. 'filename': output_filename,
  57. })
  58. for t in all_types:
  59. write_template(f, 'tools/applib_malloc_type.template.h', t.__dict__)
  60. def generate_implementation(data, output_filename, min_sdk, disable_size_checks=False):
  61. all_types = get_types(data)
  62. with open(output_filename, 'w') as f:
  63. includes = ['#include "%s"' % h for h in data['headers']]
  64. applib_enum_types = ['ApplibType_%s' % t.name for t in all_types]
  65. applib_malloc_types = ['{ sizeof(%s), %u, %u }' % (t.name, t.size_2x, t.size_3x)
  66. for t in all_types]
  67. write_template(f, 'tools/applib_malloc.template.c', {
  68. 'filename': os.path.basename(output_filename),
  69. 'includes': '\n'.join(includes),
  70. 'applib_enum_types': ',\n '.join(applib_enum_types),
  71. 'applib_malloc_types': ',\n '.join(applib_malloc_types),
  72. })
  73. for t in all_types:
  74. t.min_sdk = min_sdk
  75. t.check_size = 0 if disable_size_checks else 1
  76. t.get_total_3x_padding(all_types) # Populate the value
  77. write_template(f, 'tools/applib_malloc_type.template.c', t.__dict__)
  78. def generate_files(json_filename, header_filename, impl_filename, min_sdk,
  79. disable_size_checks=False):
  80. with open(json_filename) as f:
  81. data = json.load(f)
  82. generate_header(data, header_filename)
  83. generate_implementation(data, impl_filename, min_sdk, disable_size_checks)
  84. def _get_sizeof_type(elf_filename, typename):
  85. def _run_gdb(cmd):
  86. running_cmd = sh.arm_none_eabi_gdb(elf_filename, batch=True, nx=True, ex=cmd)
  87. result = str(running_cmd)
  88. # Strip escape sequences if present
  89. if result[0] == '\x1b':
  90. result = result[8:]
  91. return result.strip()
  92. gdb_output = _run_gdb('p sizeof(%s)' % typename)
  93. if len(gdb_output) == 0:
  94. # Sometimes gdb is dumb and fails at interpreting a typedef, try again with a struct prefix
  95. gdb_output = _run_gdb('p sizeof(struct %s)' % typename)
  96. if len(gdb_output) == 0:
  97. raise Exception("Failed to get sizeof for type %s" % typename)
  98. # Looks like "$1 = 44", we want the "44"
  99. return int(gdb_output.split()[2])
  100. def dump_sizes(json_filename, elf_filename):
  101. with open(json_filename) as f:
  102. data = json.load(f)
  103. all_types = get_types(data)
  104. fmt_str = "%30s %10s %10s %10s %16s %16s %16s %s"
  105. print(fmt_str % ("Type", "sizeof()", "Size 2.x", "Size 3.x",
  106. "direct padding", "total padding", "calculated size",
  107. "dependencies"))
  108. for t in all_types:
  109. type_sizeof = _get_sizeof_type(elf_filename, t.name)
  110. calculated_size = type_sizeof + t.get_total_3x_padding(all_types)
  111. if not t.size_3x or calculated_size == t.size_3x:
  112. calculated_size_str = str(calculated_size)
  113. else:
  114. calculated_size_str = "%u <%u>" % (calculated_size, (calculated_size - t.size_3x))
  115. print(fmt_str % (t.name, type_sizeof, t.size_2x, t.size_3x,
  116. t.size_3x_direct_padding, t.get_total_3x_padding(all_types),
  117. calculated_size_str, t.dependencies))
  118. if __name__ == "__main__":
  119. parser = argparse.ArgumentParser()
  120. parser.add_argument('--json', type=str, default='src/fw/applib/applib_malloc.json',
  121. help="Specify the JSON file to use")
  122. parser.add_argument('--elf', type=str, default='build/src/fw/tintin_fw.elf',
  123. help="Specify the ELF file to use")
  124. args = parser.parse_args()
  125. dump_sizes(args.json, args.elf)