blob: 34ecb0386144c2e3d7912ae2cc6c6032b2b62023 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001BUILDSTATS_BASE = "${TMPDIR}/buildstats/"
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002
3################################################################################
4# Build statistics gathering.
5#
6# The CPU and Time gathering/tracking functions and bbevent inspiration
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05007# were written by Christopher Larson.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05008#
9################################################################################
10
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050011def get_buildprocess_cputime(pid):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012 with open("/proc/%d/stat" % pid, "r") as f:
13 fields = f.readline().rstrip().split()
14 # 13: utime, 14: stime, 15: cutime, 16: cstime
15 return sum(int(field) for field in fields[13:16])
16
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050017def get_process_cputime(pid):
18 import resource
19 with open("/proc/%d/stat" % pid, "r") as f:
20 fields = f.readline().rstrip().split()
21 stats = {
22 'utime' : fields[13],
23 'stime' : fields[14],
24 'cutime' : fields[15],
25 'cstime' : fields[16],
26 }
27 iostats = {}
28 if os.path.isfile("/proc/%d/io" % pid):
29 with open("/proc/%d/io" % pid, "r") as f:
30 while True:
31 i = f.readline().strip()
32 if not i:
33 break
34 i = i.split(": ")
35 iostats[i[0]] = i[1]
36 resources = resource.getrusage(resource.RUSAGE_SELF)
37 childres = resource.getrusage(resource.RUSAGE_CHILDREN)
38 return stats, iostats, resources, childres
39
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040def get_cputime():
41 with open("/proc/stat", "r") as f:
42 fields = f.readline().rstrip().split()[1:]
43 return sum(int(field) for field in fields)
44
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050045def set_timedata(var, d, server_time):
46 d.setVar(var, server_time)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050047
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050048def get_timedata(var, d, end_time):
49 oldtime = d.getVar(var, False)
50 if oldtime is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050051 return
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050052 return end_time - oldtime
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050054def set_buildtimedata(var, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055 import time
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050056 time = time.time()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057 cputime = get_cputime()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050058 proctime = get_buildprocess_cputime(os.getpid())
59 d.setVar(var, (time, cputime, proctime))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050061def get_buildtimedata(var, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062 import time
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050063 timedata = d.getVar(var, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050064 if timedata is None:
65 return
66 oldtime, oldcpu, oldproc = timedata
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050067 procdiff = get_buildprocess_cputime(os.getpid()) - oldproc
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068 cpudiff = get_cputime() - oldcpu
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050069 end_time = time.time()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070 timediff = end_time - oldtime
71 if cpudiff > 0:
72 cpuperc = float(procdiff) * 100 / cpudiff
73 else:
74 cpuperc = None
75 return timediff, cpuperc
76
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050077def write_task_data(status, logfile, e, d):
78 bn = d.getVar('BUILDNAME', True)
79 bsdir = os.path.join(d.getVar('BUILDSTATS_BASE', True), bn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080 with open(os.path.join(logfile), "a") as f:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050081 elapsedtime = get_timedata("__timedata_task", d, e.time)
82 if elapsedtime:
83 f.write(d.expand("${PF}: %s: Elapsed time: %0.2f seconds \n" %
84 (e.task, elapsedtime)))
85 cpu, iostats, resources, childres = get_process_cputime(os.getpid())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086 if cpu:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050087 f.write("utime: %s\n" % cpu['utime'])
88 f.write("stime: %s\n" % cpu['stime'])
89 f.write("cutime: %s\n" % cpu['cutime'])
90 f.write("cstime: %s\n" % cpu['cstime'])
91 for i in iostats:
92 f.write("IO %s: %s\n" % (i, iostats[i]))
93 rusages = ["ru_utime", "ru_stime", "ru_maxrss", "ru_minflt", "ru_majflt", "ru_inblock", "ru_oublock", "ru_nvcsw", "ru_nivcsw"]
94 for i in rusages:
95 f.write("rusage %s: %s\n" % (i, getattr(resources, i)))
96 for i in rusages:
97 f.write("Child rusage %s: %s\n" % (i, getattr(childres, i)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 if status is "passed":
99 f.write("Status: PASSED \n")
100 else:
101 f.write("Status: FAILED \n")
102 f.write("Ended: %0.2f \n" % e.time)
103
104python run_buildstats () {
105 import bb.build
106 import bb.event
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500107 import time, subprocess, platform
108
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500109 bn = d.getVar('BUILDNAME', True)
110 bsdir = os.path.join(d.getVar('BUILDSTATS_BASE', True), bn)
111 taskdir = os.path.join(bsdir, d.getVar('PF', True))
112
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500113 if isinstance(e, bb.event.BuildStarted):
114 ########################################################################
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500115 # If the kernel was not configured to provide I/O statistics, issue
116 # a one time warning.
117 ########################################################################
118 if not os.path.isfile("/proc/%d/io" % os.getpid()):
119 bb.warn("The Linux kernel on your build host was not configured to provide process I/O statistics. (CONFIG_TASK_IO_ACCOUNTING is not set)")
120
121 ########################################################################
122 # at first pass make the buildstats hierarchy and then
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123 # set the buildname
124 ########################################################################
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500125 bb.utils.mkdirhier(bsdir)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500126 set_buildtimedata("__timedata_build", d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127 build_time = os.path.join(bsdir, "build_stats")
128 # write start of build into build_time
129 with open(build_time, "a") as f:
130 host_info = platform.uname()
131 f.write("Host Info: ")
132 for x in host_info:
133 if x:
134 f.write(x + " ")
135 f.write("\n")
136 f.write("Build Started: %0.2f \n" % time.time())
137
138 elif isinstance(e, bb.event.BuildCompleted):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 build_time = os.path.join(bsdir, "build_stats")
140 with open(build_time, "a") as f:
141 ########################################################################
142 # Write build statistics for the build
143 ########################################################################
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500144 timedata = get_buildtimedata("__timedata_build", d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145 if timedata:
146 time, cpu = timedata
147 # write end of build and cpu used into build_time
148 f.write("Elapsed time: %0.2f seconds \n" % (time))
149 if cpu:
150 f.write("CPU usage: %0.1f%% \n" % cpu)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151
152 if isinstance(e, bb.build.TaskStarted):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500153 set_timedata("__timedata_task", d, e.time)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500154 bb.utils.mkdirhier(taskdir)
155 # write into the task event file the name and start time
156 with open(os.path.join(taskdir, e.task), "a") as f:
157 f.write("Event: %s \n" % bb.event.getName(e))
158 f.write("Started: %0.2f \n" % e.time)
159
160 elif isinstance(e, bb.build.TaskSucceeded):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500161 write_task_data("passed", os.path.join(taskdir, e.task), e, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162 if e.task == "do_rootfs":
163 bs = os.path.join(bsdir, "build_stats")
164 with open(bs, "a") as f:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500165 rootfs = d.getVar('IMAGE_ROOTFS', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166 rootfs_size = subprocess.Popen(["du", "-sh", rootfs], stdout=subprocess.PIPE).stdout.read()
167 f.write("Uncompressed Rootfs size: %s" % rootfs_size)
168
169 elif isinstance(e, bb.build.TaskFailed):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500170 # Can have a failure before TaskStarted so need to mkdir here too
171 bb.utils.mkdirhier(taskdir)
172 write_task_data("failed", os.path.join(taskdir, e.task), e, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500173 ########################################################################
174 # Lets make things easier and tell people where the build failed in
175 # build_status. We do this here because BuildCompleted triggers no
176 # matter what the status of the build actually is
177 ########################################################################
178 build_status = os.path.join(bsdir, "build_stats")
179 with open(build_status, "a") as f:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500180 f.write(d.expand("Failed at: ${PF} at task: %s \n" % e.task))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500181}
182
183addhandler run_buildstats
184run_buildstats[eventmask] = "bb.event.BuildStarted bb.event.BuildCompleted bb.build.TaskStarted bb.build.TaskSucceeded bb.build.TaskFailed"
185