1""" 2Function tracer profiler for autotest. 3 4@author: David Sharp (dhsharp@google.com) 5""" 6import logging, os, signal, time 7from autotest_lib.client.bin import profiler, utils 8from autotest_lib.client.common_lib import error 9 10 11class ftrace(profiler.profiler): 12 """ 13 ftrace profiler for autotest. It builds ftrace from souce and runs 14 trace-cmd with configurable parameters. 15 16 @see: git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git 17 """ 18 version = 1 19 20 mountpoint = '/sys/kernel/debug' 21 tracing_dir = os.path.join(mountpoint, 'tracing') 22 23 @staticmethod 24 def join_command(cmd): 25 """ 26 Shell escape the command for BgJob. grmbl. 27 28 @param cmd: Command list. 29 """ 30 result = [] 31 for arg in cmd: 32 arg = '"%s"' % utils.sh_escape(arg) 33 result += [arg] 34 return ' '.join(result) 35 36 37 def setup(self, tarball='trace-cmd.tar.bz2', **kwargs): 38 """ 39 Build and install trace-cmd from source. 40 41 The tarball was obtained by checking the git repo at 09-14-2010, 42 removing the Documentation and the .git folders, and compressing 43 it. 44 45 @param tarball: Path to trace-cmd tarball. 46 @param **kwargs: Dictionary with additional parameters. 47 """ 48 self.tarball = utils.unmap_url(self.bindir, tarball, self.tmpdir) 49 utils.extract_tarball_to_dir(self.tarball, self.srcdir) 50 os.chdir(self.srcdir) 51 utils.make("prefix='%s'" % self.builddir) 52 utils.make("prefix='%s' install" % self.builddir) 53 54 55 def initialize(self, tracepoints, buffer_size_kb=1408, **kwargs): 56 """ 57 Initialize ftrace profiler. 58 59 @param tracepoints: List containing a mix of tracpoint names and 60 (tracepoint name, filter) tuples. Tracepoint names are as 61 accepted by trace-cmd -e, eg "syscalls", or 62 "syscalls:sys_enter_read". Filters are as accepted by 63 trace-cmd -f, eg "((sig >= 10 && sig < 15) || sig == 17)" 64 @param buffer_size_kb: Set the size of the ring buffer (per cpu). 65 """ 66 self.job.require_gcc() 67 self.trace_cmd_args = ['-b', str(buffer_size_kb)] 68 for tracepoint in tracepoints: 69 if isinstance(tracepoint, tuple): 70 tracepoint, event_filter = tracepoint 71 else: 72 event_filter = None 73 self.trace_cmd_args += ['-e', tracepoint] 74 if event_filter: 75 self.trace_cmd_args += ['-f', event_filter] 76 77 self.builddir = os.path.join(self.bindir, 'build') 78 if not os.path.isdir(self.builddir): 79 os.makedirs(self.builddir) 80 self.trace_cmd = os.path.join(self.builddir, 'bin', 'trace-cmd') 81 82 83 def start(self, test): 84 """ 85 Start ftrace profiler 86 87 @param test: Autotest test in which the profiler will operate on. 88 """ 89 # Make sure debugfs is mounted and tracing disabled. 90 utils.system('%s reset' % self.trace_cmd) 91 92 output_dir = os.path.join(test.profdir, 'ftrace') 93 if not os.path.isdir(output_dir): 94 os.makedirs(output_dir) 95 self.output = os.path.join(output_dir, 'trace.dat') 96 cmd = [self.trace_cmd, 'record', '-o', self.output] 97 cmd += self.trace_cmd_args 98 self.record_job = utils.BgJob(self.join_command(cmd), 99 stderr_tee=utils.TEE_TO_LOGS) 100 101 # Wait for tracing to be enabled. If trace-cmd dies before enabling 102 # tracing, then there was a problem. 103 tracing_on = os.path.join(self.tracing_dir, 'tracing_on') 104 while (self.record_job.sp.poll() is None and 105 utils.read_file(tracing_on).strip() != '1'): 106 time.sleep(0.1) 107 if self.record_job.sp.poll() is not None: 108 utils.join_bg_jobs([self.record_job]) 109 raise error.CmdError(self.record_job.command, 110 self.record_job.sp.returncode, 111 'trace-cmd exited early.') 112 113 def stop(self, test): 114 """ 115 Stop ftrace profiler. 116 117 @param test: Autotest test in which the profiler will operate on. 118 """ 119 os.kill(self.record_job.sp.pid, signal.SIGINT) 120 utils.join_bg_jobs([self.record_job]) 121 # shrink the buffer to free memory. 122 utils.system('%s reset -b 1' % self.trace_cmd) 123 124 #compress output 125 utils.system('bzip2 %s' % self.output) 126 compressed_output = self.output + '.bz2' 127 # if the compressed trace file is large (10MB), just delete it. 128 compressed_output_size = os.path.getsize(compressed_output) 129 if compressed_output_size > 10*1024*1024: 130 logging.warning('Deleting large trace file %s (%d bytes)', 131 compressed_output, compressed_output_size) 132 os.remove(compressed_output) 133 # remove per-cpu files in case trace-cmd died. 134 utils.system('rm -f %s.cpu*' % self.output) 135