benchgen.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. #!/usr/bin/env python
  2. # Copyright (C) 2015 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the 'License');
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an 'AS IS' BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """
  16. Generates storage benchmark from captured strace output.
  17. Currently assumes that all mmap'ed regions are resource accesses, and emulates as pread().
  18. Usage:
  19. $ adb shell strace -p `pid zygote` -o /data/local/tmp/trace -f -ff -y -ttt -e trace=file,desc,munmap
  20. $ adb pull /data/local/tmp/trace*
  21. $ python benchgen.py trace.*
  22. """
  23. import re, sys, collections, traceback, argparse
  24. from operator import itemgetter
  25. from collections import defaultdict
  26. class Event:
  27. def __init__(self, thread, time, call, args, ret):
  28. self.thread = thread
  29. self.time = time
  30. self.call = call
  31. self.args = args
  32. self.ret = ret
  33. def __repr__(self):
  34. return "%s(%s)=%s" % (self.call, repr(self.args), self.ret)
  35. class File:
  36. def __init__(self, name, ident):
  37. self.name = name
  38. self.ident = ident
  39. self.size = 0
  40. def __repr__(self):
  41. return self.name
  42. events = []
  43. files = {}
  44. def find_file(name):
  45. name = name.strip('<>"')
  46. if name not in files:
  47. files[name] = File(name, len(files))
  48. return files[name]
  49. def extract_file(e, arg):
  50. if "<" in arg:
  51. fd, path = arg.split("<")
  52. path = path.strip(">")
  53. handle = "t%sf%s" % (e.thread, fd)
  54. return (fd, find_file(path), handle)
  55. else:
  56. return (None, None, None)
  57. def parse_args(s):
  58. args = []
  59. arg = ""
  60. esc = False
  61. quot = False
  62. for c in s:
  63. if esc:
  64. esc = False
  65. arg += c
  66. continue
  67. if c == '"':
  68. if quot:
  69. quot = False
  70. continue
  71. else:
  72. quot = True
  73. continue
  74. if c == '\\':
  75. esc = True
  76. continue
  77. if c == ',' and not quot:
  78. args.append(arg.strip())
  79. arg = ""
  80. else:
  81. arg += c
  82. args.append(arg.strip())
  83. return args
  84. bufsize = 1048576
  85. interesting = ["mmap2","read","write","pread64","pwrite64","fsync","fdatasync","openat","close","lseek","_llseek"]
  86. re_event = re.compile(r"^([\d\.]+) (.+?)\((.+?)\) = (.+?)$")
  87. re_arg = re.compile(r'''((?:[^,"']|"[^"]*"|'[^']*')+)''')
  88. for fn in sys.argv[1:]:
  89. with open(fn) as f:
  90. thread = int(fn.split(".")[-1])
  91. for line in f:
  92. line = re_event.match(line)
  93. if not line: continue
  94. time, call, args, ret = line.groups()
  95. if call not in interesting: continue
  96. if "/data/" not in args: continue
  97. time = float(time)
  98. args = parse_args(args)
  99. events.append(Event(thread, time, call, args, ret))
  100. with open("BenchmarkGen.h", 'w') as bench:
  101. print >>bench, """/*
  102. * Copyright (C) 2015 The Android Open Source Project
  103. *
  104. * Licensed under the Apache License, Version 2.0 (the "License");
  105. * you may not use this file except in compliance with the License.
  106. * You may obtain a copy of the License at
  107. *
  108. * http://www.apache.org/licenses/LICENSE-2.0
  109. *
  110. * Unless required by applicable law or agreed to in writing, software
  111. * distributed under the License is distributed on an "AS IS" BASIS,
  112. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  113. * See the License for the specific language governing permissions and
  114. * limitations under the License.
  115. */
  116. /******************************************************************
  117. * THIS CODE WAS GENERATED BY benchgen.py, DO NOT MODIFY DIRECTLY *
  118. ******************************************************************/
  119. #include <android-base/logging.h>
  120. #include <stdlib.h>
  121. #include <sys/types.h>
  122. #include <sys/stat.h>
  123. #include <sys/sendfile.h>
  124. #include <fcntl.h>
  125. #include <algorithm>
  126. #include <functional>
  127. #include <string>
  128. #include <Utils.h>
  129. namespace android {
  130. namespace vold {
  131. static status_t BenchmarkRun(std::function<bool(int)> checkpoint) {
  132. """
  133. print >>bench, "char* buf = (char*) malloc(%d);" % (bufsize)
  134. nread = 0
  135. nwrite = 0
  136. nsync = 0
  137. events = sorted(events, key=lambda e: e.time)
  138. active = set()
  139. defined = set()
  140. i = 0
  141. total = len(events)
  142. for e in events:
  143. i += 1
  144. if i % 256 == 0:
  145. print >>bench, "if (!checkpoint(%d)) return -1;" % (50 + ((i * 50) / total))
  146. if e.call == "openat":
  147. fd, f, handle = extract_file(e, e.ret)
  148. if f:
  149. active.add(handle)
  150. if handle not in defined:
  151. print >>bench, "int",
  152. defined.add(handle)
  153. create_mode = ''
  154. if 'O_CREAT' in e.args[2]:
  155. assert len(e.args) > 3, 'File creation lacks a mode?'
  156. create_mode = ', ' + e.args[3]
  157. print >>bench, '%s = TEMP_FAILURE_RETRY(open("file%s", %s%s));' \
  158. % (handle, f.ident, e.args[2], create_mode)
  159. elif e.call == "close":
  160. fd, f, handle = extract_file(e, e.args[0])
  161. if handle in active:
  162. active.remove(handle)
  163. print >>bench, 'close(%s);' % (handle)
  164. elif e.call == "lseek":
  165. fd, f, handle = extract_file(e, e.args[0])
  166. if handle in active:
  167. print >>bench, 'TEMP_FAILURE_RETRY(lseek(%s, %s, %s));' % (handle, e.args[1], e.args[2])
  168. elif e.call == "_llseek":
  169. fd, f, handle = extract_file(e, e.args[0])
  170. if handle in active:
  171. print >>bench, 'TEMP_FAILURE_RETRY(lseek(%s, %s, %s));' % (handle, e.args[1], e.args[3])
  172. elif e.call == "read":
  173. fd, f, handle = extract_file(e, e.args[0])
  174. if handle in active:
  175. # TODO: track actual file size instead of guessing
  176. count = min(int(e.args[2]), bufsize)
  177. f.size += count
  178. print >>bench, 'TEMP_FAILURE_RETRY(read(%s, buf, %d));' % (handle, count)
  179. nread += 1
  180. elif e.call == "write":
  181. fd, f, handle = extract_file(e, e.args[0])
  182. if handle in active:
  183. # TODO: track actual file size instead of guessing
  184. count = min(int(e.args[2]), bufsize)
  185. f.size += count
  186. print >>bench, 'TEMP_FAILURE_RETRY(write(%s, buf, %d));' % (handle, count)
  187. nwrite += 1
  188. elif e.call == "pread64":
  189. fd, f, handle = extract_file(e, e.args[0])
  190. if handle in active:
  191. f.size = max(f.size, int(e.args[2]) + int(e.args[3]))
  192. count = min(int(e.args[2]), bufsize)
  193. print >>bench, 'TEMP_FAILURE_RETRY(pread(%s, buf, %d, %s));' % (handle, count, e.args[3])
  194. nread += 1
  195. elif e.call == "pwrite64":
  196. fd, f, handle = extract_file(e, e.args[0])
  197. if handle in active:
  198. f.size = max(f.size, int(e.args[2]) + int(e.args[3]))
  199. count = min(int(e.args[2]), bufsize)
  200. print >>bench, 'TEMP_FAILURE_RETRY(pwrite(%s, buf, %d, %s));' % (handle, count, e.args[3])
  201. nwrite += 1
  202. elif e.call == "fsync":
  203. fd, f, handle = extract_file(e, e.args[0])
  204. if handle in active:
  205. print >>bench, 'TEMP_FAILURE_RETRY(fsync(%s));' % (handle)
  206. nsync += 1
  207. elif e.call == "fdatasync":
  208. fd, f, handle = extract_file(e, e.args[0])
  209. if handle in active:
  210. print >>bench, 'TEMP_FAILURE_RETRY(fdatasync(%s));' % (handle)
  211. nsync += 1
  212. elif e.call == "mmap2":
  213. fd, f, handle = extract_file(e, e.args[4])
  214. if handle in active:
  215. count = min(int(e.args[1]), bufsize)
  216. offset = int(e.args[5], 0)
  217. f.size = max(f.size, count + offset)
  218. print >>bench, 'TEMP_FAILURE_RETRY(pread(%s, buf, %s, %s)); // mmap2' % (handle, count, offset)
  219. nread += 1
  220. for handle in active:
  221. print >>bench, 'close(%s);' % (handle)
  222. print >>bench, """
  223. free(buf);
  224. return 0;
  225. }
  226. static status_t CreateFile(const char* name, int len) {
  227. int chunk = std::min(len, 65536);
  228. int out = -1;
  229. std::string buf;
  230. if (android::vold::ReadRandomBytes(chunk, buf) != OK) {
  231. LOG(ERROR) << "Failed to read random data";
  232. return -EIO;
  233. }
  234. if ((out = TEMP_FAILURE_RETRY(open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644))) < 0) {
  235. PLOG(ERROR) << "Failed to open " << name;
  236. return -errno;
  237. }
  238. while (len > 0) {
  239. int n = write(out, buf.c_str(), std::min(len, chunk));
  240. if (n < 0) {
  241. PLOG(ERROR) << "Failed to write";
  242. close(out);
  243. return -errno;
  244. }
  245. len -= n;
  246. }
  247. close(out);
  248. return OK;
  249. }
  250. static status_t BenchmarkCreate(std::function<bool(int)> checkpoint) {
  251. status_t res = 0;
  252. res |= CreateFile("stub", 0);
  253. """
  254. i = 0
  255. total = len(files.values())
  256. for f in files.values():
  257. i += 1
  258. if i % 12 == 0:
  259. print >>bench, "if (!checkpoint(%d)) return -1;" % ((i * 50) / total)
  260. print >>bench, 'res |= CreateFile("file%s", %d);' % (f.ident, f.size)
  261. print >>bench, """
  262. return res;
  263. }
  264. static status_t BenchmarkDestroy() {
  265. status_t res = 0;
  266. res |= unlink("stub");
  267. """
  268. for f in files.values():
  269. print >>bench, 'res |= unlink("file%s");' % (f.ident)
  270. print >>bench, """
  271. return res;
  272. }
  273. static std::string BenchmarkIdent() {"""
  274. print >>bench, """return "r%d:w%d:s%d";""" % (nread, nwrite, nsync)
  275. print >>bench, """}
  276. } // namespace vold
  277. } // namespace android
  278. """
  279. size = sum([ f.size for f in files.values() ])
  280. print "Found", len(files), "data files accessed, total size", (size/1024), "kB"
  281. types = defaultdict(int)
  282. for e in events:
  283. types[e.call] += 1
  284. print "Found syscalls:"
  285. for t, n in types.iteritems():
  286. print str(n).rjust(8), t
  287. print