diff options
author | Beth Flanagan <elizabeth.flanagan@intel.com> | 2011-02-12 16:25:09 -0800 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2011-02-15 09:59:51 +0000 |
commit | 411056041b23631548ad2fa467152eb52c32a0ea (patch) | |
tree | ea5ba9a9475b8c8191a579a965c23d5f4504c90e /meta/classes/buildstats.bbclass | |
parent | 6cb8fd6def4912e4aa76330649ba42a9ed2694fd (diff) | |
download | poky-411056041b23631548ad2fa467152eb52c32a0ea.tar.gz |
Buildstats commit: buildstats.bbclass
Used to track some basic build metrics by build and task/event level.
Signed-off-by: Beth Flanagan <elizabeth.flanagan@intel.com>
Diffstat (limited to 'meta/classes/buildstats.bbclass')
-rw-r--r-- | meta/classes/buildstats.bbclass | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/meta/classes/buildstats.bbclass b/meta/classes/buildstats.bbclass new file mode 100644 index 0000000000..f57d018060 --- /dev/null +++ b/meta/classes/buildstats.bbclass | |||
@@ -0,0 +1,194 @@ | |||
1 | BUILDSTATS_BASE = ${TMPDIR}/buildstats/ | ||
2 | BNFILE = ${BUILDSTATS_BASE}/.buildname | ||
3 | |||
4 | ################################################################################ | ||
5 | # Build statistics gathering. | ||
6 | # | ||
7 | # The CPU and Time gathering/tracking functions and bbevent inspiration | ||
8 | # were written by Christopher Larson and can be seen here: | ||
9 | # http://kergoth.pastey.net/142813 | ||
10 | # | ||
11 | ################################################################################ | ||
12 | |||
13 | def get_process_cputime(pid): | ||
14 | fields = open("/proc/%d/stat" % pid, "r").readline().rstrip().split() | ||
15 | # 13: utime, 14: stime, 15: cutime, 16: cstime | ||
16 | return sum(int(field) for field in fields[13:16]) | ||
17 | |||
18 | def get_cputime(): | ||
19 | fields = open("/proc/stat", "r").readline().rstrip().split()[1:] | ||
20 | return sum(int(field) for field in fields) | ||
21 | |||
22 | def set_timedata(var, data): | ||
23 | import time | ||
24 | |||
25 | time = time.time() | ||
26 | cputime = get_cputime() | ||
27 | proctime = get_process_cputime(os.getpid()) | ||
28 | data.setVar(var, (time, cputime, proctime)) | ||
29 | |||
30 | def get_timedata(var, data): | ||
31 | import time | ||
32 | timedata = data.getVar(var, False) | ||
33 | if timedata is None: | ||
34 | return | ||
35 | oldtime, oldcpu, oldproc = timedata | ||
36 | procdiff = get_process_cputime(os.getpid()) - oldproc | ||
37 | cpudiff = get_cputime() - oldcpu | ||
38 | timediff = time.time() - oldtime | ||
39 | if cpudiff > 0: | ||
40 | cpuperc = float(procdiff) * 100 / cpudiff | ||
41 | else: | ||
42 | cpuperc = None | ||
43 | return timediff, cpuperc | ||
44 | |||
45 | ############################################## | ||
46 | # We need to set the buildname to a file since | ||
47 | # BUILDNAME changes throughout a build | ||
48 | ############################################## | ||
49 | |||
50 | def set_bn(e): | ||
51 | bn = e.getPkgs()[0] + "-" + bb.data.getVar('MACHINE',e.data, True) | ||
52 | try: | ||
53 | os.remove(bb.data.getVar('BNFILE',e.data, True)) | ||
54 | except: | ||
55 | pass | ||
56 | file = open(bb.data.getVar('BNFILE',e.data, True), "w") | ||
57 | file.write(os.path.join(bn, bb.data.getVar('BUILDNAME', e.data, True))) | ||
58 | file.close() | ||
59 | |||
60 | def get_bn(e): | ||
61 | file = open(bb.data.getVar('BNFILE',e.data, True)) | ||
62 | bn = file.readline() | ||
63 | file.close() | ||
64 | return bn | ||
65 | |||
66 | python run_buildstats () { | ||
67 | import bb.build | ||
68 | import bb.event | ||
69 | import bb.data | ||
70 | import time, subprocess | ||
71 | |||
72 | if isinstance(e, bb.event.BuildStarted): | ||
73 | ############################################## | ||
74 | # at first pass make the buildstats heriarchy and then | ||
75 | # set the buildname | ||
76 | ############################################## | ||
77 | try: | ||
78 | bb.mkdirhier(bb.data.getVar('BUILDSTATS_BASE', e.data, True)) | ||
79 | except: | ||
80 | pass | ||
81 | set_bn(e) | ||
82 | bn = get_bn(e) | ||
83 | bb.warn(bn) | ||
84 | bsdir = os.path.join(bb.data.getVar('BUILDSTATS_BASE', e.data, True), bn) | ||
85 | try: | ||
86 | bb.mkdirhier(bsdir) | ||
87 | except: | ||
88 | pass | ||
89 | set_timedata("__timedata_build", e.data) | ||
90 | build_time = os.path.join(bsdir, "build_stats") | ||
91 | # write start of build into build_time | ||
92 | file = open(build_time,"a") | ||
93 | # We do this here because subprocess within BuildStarted is messy | ||
94 | host_info = subprocess.Popen(["uname", "-a"], stdout=subprocess.PIPE).stdout.read() | ||
95 | file.write("Host Info: %s" % host_info) | ||
96 | file.write("Build Started: %0.2f \n" % time.time()) | ||
97 | file.close() | ||
98 | |||
99 | elif isinstance(e, bb.event.BuildCompleted): | ||
100 | bn=get_bn(e) | ||
101 | timedata = get_timedata("__timedata_build", e.data) | ||
102 | if not timedata: | ||
103 | return | ||
104 | time, cpu = timedata | ||
105 | bsdir = os.path.join(bb.data.getVar('BUILDSTATS_BASE', e.data, True), bn) | ||
106 | build_time = os.path.join(bsdir, "build_stats") | ||
107 | # write end of build and cpu used into build_time | ||
108 | file = open(build_time, "a") | ||
109 | file.write("Elapsed time: %0.2f seconds \n" % (time)) | ||
110 | if cpu: | ||
111 | file.write("CPU usage: %0.1f%% \n" % cpu) | ||
112 | file.close() | ||
113 | |||
114 | |||
115 | if isinstance(e, bb.build.TaskStarted): | ||
116 | bn=get_bn(e) | ||
117 | set_timedata("__timedata_task", e.data) | ||
118 | |||
119 | bsdir = os.path.join(bb.data.getVar('BUILDSTATS_BASE', e.data, True), bn) | ||
120 | taskdir = os.path.join(bsdir, bb.data.expand("${PF}", e.data)) | ||
121 | try: | ||
122 | bb.mkdirhier(taskdir) | ||
123 | except: | ||
124 | pass | ||
125 | # write into the task event file the name and start time | ||
126 | file = open(os.path.join(taskdir, e.task), "a") | ||
127 | file.write("Event: %s \n" % bb.event.getName(e)) | ||
128 | file.write("Started: %0.2f \n" % time.time()) | ||
129 | file.close() | ||
130 | |||
131 | elif isinstance(e, bb.build.TaskSucceeded): | ||
132 | bn=get_bn(e) | ||
133 | timedata = get_timedata("__timedata_task", e.data) | ||
134 | if not timedata: | ||
135 | return | ||
136 | time, cpu = timedata | ||
137 | bsdir = os.path.join(bb.data.getVar('BUILDSTATS_BASE', e.data, True), bn) | ||
138 | taskdir = os.path.join(bsdir, bb.data.expand("${PF}", e.data)) | ||
139 | file = open(os.path.join(taskdir, e.task), "a") | ||
140 | file.write(bb.data.expand("${PF}: %s: Elapsed time: %0.2f seconds \n" % | ||
141 | (e.task, time), e.data)) | ||
142 | if cpu: | ||
143 | file.write("CPU usage: %0.1f%% \n" % cpu) | ||
144 | |||
145 | file.write("Status: PASSED") | ||
146 | file.close() | ||
147 | |||
148 | ############################################## | ||
149 | # Alot of metric gathering occurs here. | ||
150 | # Reminder: I stripped out some in process stuff here | ||
151 | ############################################## | ||
152 | |||
153 | if e.task == "do_rootfs": | ||
154 | bs=os.path.join(bsdir, "build_stats") | ||
155 | file = open(bs,"a") | ||
156 | rootfs = bb.data.getVar('IMAGE_ROOTFS', e.data, True) | ||
157 | rootfs_size = subprocess.Popen(["du", "-sh", rootfs], stdout=subprocess.PIPE).stdout.read() | ||
158 | file.write("Uncompressed Rootfs size: %s" % rootfs_size) | ||
159 | file.close() | ||
160 | |||
161 | elif isinstance(e, bb.build.TaskFailed): | ||
162 | bn=get_bn(e) | ||
163 | timedata = get_timedata("__timedata_task", e.data) | ||
164 | if not timedata: | ||
165 | return | ||
166 | time, cpu = timedata | ||
167 | bsdir = os.path.join(bb.data.getVar('BUILDSTATS_BASE', e.data, True), bn) | ||
168 | taskdir = os.path.join(bsdir, bb.data.expand("${PF}", e.data)) | ||
169 | ############################################## | ||
170 | # If the task fails dump the regular data. | ||
171 | # fgrep -R "FAILED" <bsdir> | ||
172 | # will grep all the events that failed. | ||
173 | ############################################## | ||
174 | file = open(os.path.join(taskdir, e.task), "a") | ||
175 | file.write(bb.data.expand("${PF}: %s: Elapsed time: %0.2f seconds \n" % | ||
176 | (e.task, time), e.data)) | ||
177 | if cpu: | ||
178 | file.write("CPU usage: %0.1f%% \n" % cpu) | ||
179 | file.write("Status: FAILED") | ||
180 | file.close() | ||
181 | ############################################## | ||
182 | # Lets make things easier and tell people where the build failed in build_status | ||
183 | # We do this here because BuildCompleted triggers no matter what the status of the | ||
184 | # build actually is | ||
185 | ############################################## | ||
186 | build_status = os.path.join(bsdir, "build_stats") | ||
187 | file = open(build_status,"a") | ||
188 | file.write(bb.data.expand("Failed at: ${PF} at task: %s \n", e.task)) | ||
189 | file.close() | ||
190 | |||
191 | } | ||
192 | |||
193 | addhandler run_buildstats | ||
194 | |||