1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Toaster helper class 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# Copyright (C) 2013 Intel Corporation 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun# Released under the MIT license (see COPYING.MIT) 7*4882a593Smuzhiyun# 8*4882a593Smuzhiyun# This bbclass is designed to extract data used by OE-Core during the build process, 9*4882a593Smuzhiyun# for recording in the Toaster system. 10*4882a593Smuzhiyun# The data access is synchronous, preserving the build data integrity across 11*4882a593Smuzhiyun# different builds. 12*4882a593Smuzhiyun# 13*4882a593Smuzhiyun# The data is transferred through the event system, using the MetadataEvent objects. 14*4882a593Smuzhiyun# 15*4882a593Smuzhiyun# The model is to enable the datadump functions as postfuncs, and have the dump 16*4882a593Smuzhiyun# executed after the real taskfunc has been executed. This prevents task signature changing 17*4882a593Smuzhiyun# is toaster is enabled or not. Build performance is not affected if Toaster is not enabled. 18*4882a593Smuzhiyun# 19*4882a593Smuzhiyun# To enable, use INHERIT in local.conf: 20*4882a593Smuzhiyun# 21*4882a593Smuzhiyun# INHERIT += "toaster" 22*4882a593Smuzhiyun# 23*4882a593Smuzhiyun# 24*4882a593Smuzhiyun# 25*4882a593Smuzhiyun# 26*4882a593Smuzhiyun 27*4882a593Smuzhiyun# Find and dump layer info when we got the layers parsed 28*4882a593Smuzhiyun 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun 31*4882a593Smuzhiyunpython toaster_layerinfo_dumpdata() { 32*4882a593Smuzhiyun import subprocess 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun def _get_git_branch(layer_path): 35*4882a593Smuzhiyun branch = subprocess.Popen("git symbolic-ref HEAD 2>/dev/null ", cwd=layer_path, shell=True, stdout=subprocess.PIPE).communicate()[0] 36*4882a593Smuzhiyun branch = branch.decode('utf-8') 37*4882a593Smuzhiyun branch = branch.replace('refs/heads/', '').rstrip() 38*4882a593Smuzhiyun return branch 39*4882a593Smuzhiyun 40*4882a593Smuzhiyun def _get_git_revision(layer_path): 41*4882a593Smuzhiyun revision = subprocess.Popen("git rev-parse HEAD 2>/dev/null ", cwd=layer_path, shell=True, stdout=subprocess.PIPE).communicate()[0].rstrip() 42*4882a593Smuzhiyun return revision 43*4882a593Smuzhiyun 44*4882a593Smuzhiyun def _get_url_map_name(layer_name): 45*4882a593Smuzhiyun """ Some layers have a different name on openembedded.org site, 46*4882a593Smuzhiyun this method returns the correct name to use in the URL 47*4882a593Smuzhiyun """ 48*4882a593Smuzhiyun 49*4882a593Smuzhiyun url_name = layer_name 50*4882a593Smuzhiyun url_mapping = {'meta': 'openembedded-core'} 51*4882a593Smuzhiyun 52*4882a593Smuzhiyun for key in url_mapping.keys(): 53*4882a593Smuzhiyun if key == layer_name: 54*4882a593Smuzhiyun url_name = url_mapping[key] 55*4882a593Smuzhiyun 56*4882a593Smuzhiyun return url_name 57*4882a593Smuzhiyun 58*4882a593Smuzhiyun def _get_layer_version_information(layer_path): 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun layer_version_info = {} 61*4882a593Smuzhiyun layer_version_info['branch'] = _get_git_branch(layer_path) 62*4882a593Smuzhiyun layer_version_info['commit'] = _get_git_revision(layer_path) 63*4882a593Smuzhiyun layer_version_info['priority'] = 0 64*4882a593Smuzhiyun 65*4882a593Smuzhiyun return layer_version_info 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun 68*4882a593Smuzhiyun def _get_layer_dict(layer_path): 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun layer_info = {} 71*4882a593Smuzhiyun layer_name = layer_path.split('/')[-1] 72*4882a593Smuzhiyun layer_url = 'http://layers.openembedded.org/layerindex/layer/{layer}/' 73*4882a593Smuzhiyun layer_url_name = _get_url_map_name(layer_name) 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun layer_info['name'] = layer_url_name 76*4882a593Smuzhiyun layer_info['local_path'] = layer_path 77*4882a593Smuzhiyun layer_info['layer_index_url'] = layer_url.format(layer=layer_url_name) 78*4882a593Smuzhiyun layer_info['version'] = _get_layer_version_information(layer_path) 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun return layer_info 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun bblayers = e.data.getVar("BBLAYERS") 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun llayerinfo = {} 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun for layer in { l for l in bblayers.strip().split(" ") if len(l) }: 88*4882a593Smuzhiyun llayerinfo[layer] = _get_layer_dict(layer) 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun 91*4882a593Smuzhiyun bb.event.fire(bb.event.MetadataEvent("LayerInfo", llayerinfo), e.data) 92*4882a593Smuzhiyun} 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun# Dump package file info data 95*4882a593Smuzhiyun 96*4882a593Smuzhiyundef _toaster_load_pkgdatafile(dirpath, filepath): 97*4882a593Smuzhiyun import json 98*4882a593Smuzhiyun import re 99*4882a593Smuzhiyun pkgdata = {} 100*4882a593Smuzhiyun with open(os.path.join(dirpath, filepath), "r") as fin: 101*4882a593Smuzhiyun for line in fin: 102*4882a593Smuzhiyun try: 103*4882a593Smuzhiyun kn, kv = line.strip().split(": ", 1) 104*4882a593Smuzhiyun m = re.match(r"^PKG:([^A-Z:]*)", kn) 105*4882a593Smuzhiyun if m: 106*4882a593Smuzhiyun pkgdata['OPKGN'] = m.group(1) 107*4882a593Smuzhiyun kn = kn.split(":")[0] 108*4882a593Smuzhiyun pkgdata[kn] = kv 109*4882a593Smuzhiyun if kn.startswith('FILES_INFO'): 110*4882a593Smuzhiyun pkgdata[kn] = json.loads(kv) 111*4882a593Smuzhiyun 112*4882a593Smuzhiyun except ValueError: 113*4882a593Smuzhiyun pass # ignore lines without valid key: value pairs 114*4882a593Smuzhiyun return pkgdata 115*4882a593Smuzhiyun 116*4882a593Smuzhiyundef _toaster_dumpdata(pkgdatadir, d): 117*4882a593Smuzhiyun """ 118*4882a593Smuzhiyun Dumps the data about the packages created by a recipe 119*4882a593Smuzhiyun """ 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun # No need to try and dumpdata if the recipe isn't generating packages 122*4882a593Smuzhiyun if not d.getVar('PACKAGES'): 123*4882a593Smuzhiyun return 124*4882a593Smuzhiyun 125*4882a593Smuzhiyun lpkgdata = {} 126*4882a593Smuzhiyun datadir = os.path.join(pkgdatadir, 'runtime') 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun # scan and send data for each generated package 129*4882a593Smuzhiyun if os.path.exists(datadir): 130*4882a593Smuzhiyun for datafile in os.listdir(datadir): 131*4882a593Smuzhiyun if not datafile.endswith('.packaged'): 132*4882a593Smuzhiyun lpkgdata = _toaster_load_pkgdatafile(datadir, datafile) 133*4882a593Smuzhiyun # Fire an event containing the pkg data 134*4882a593Smuzhiyun bb.event.fire(bb.event.MetadataEvent("SinglePackageInfo", lpkgdata), d) 135*4882a593Smuzhiyun 136*4882a593Smuzhiyunpython toaster_package_dumpdata() { 137*4882a593Smuzhiyun _toaster_dumpdata(d.getVar('PKGDESTWORK'), d) 138*4882a593Smuzhiyun} 139*4882a593Smuzhiyun 140*4882a593Smuzhiyunpython toaster_packagedata_dumpdata() { 141*4882a593Smuzhiyun # This path needs to match do_packagedata[sstate-inputdirs] 142*4882a593Smuzhiyun _toaster_dumpdata(os.path.join(d.getVar('WORKDIR'), 'pkgdata-pdata-input'), d) 143*4882a593Smuzhiyun} 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun# 2. Dump output image files information 146*4882a593Smuzhiyun 147*4882a593Smuzhiyunpython toaster_artifact_dumpdata() { 148*4882a593Smuzhiyun """ 149*4882a593Smuzhiyun Dump data about SDK variables 150*4882a593Smuzhiyun """ 151*4882a593Smuzhiyun 152*4882a593Smuzhiyun event_data = { 153*4882a593Smuzhiyun "TOOLCHAIN_OUTPUTNAME": d.getVar("TOOLCHAIN_OUTPUTNAME") 154*4882a593Smuzhiyun } 155*4882a593Smuzhiyun 156*4882a593Smuzhiyun bb.event.fire(bb.event.MetadataEvent("SDKArtifactInfo", event_data), d) 157*4882a593Smuzhiyun} 158*4882a593Smuzhiyun 159*4882a593Smuzhiyun# collect list of buildstats files based on fired events; when the build completes, collect all stats and fire an event with collected data 160*4882a593Smuzhiyun 161*4882a593Smuzhiyunpython toaster_collect_task_stats() { 162*4882a593Smuzhiyun import bb.build 163*4882a593Smuzhiyun import bb.event 164*4882a593Smuzhiyun import bb.data 165*4882a593Smuzhiyun import bb.utils 166*4882a593Smuzhiyun import os 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun if not e.data.getVar('BUILDSTATS_BASE'): 169*4882a593Smuzhiyun return # if we don't have buildstats, we cannot collect stats 170*4882a593Smuzhiyun 171*4882a593Smuzhiyun toaster_statlist_file = os.path.join(e.data.getVar('BUILDSTATS_BASE'), "toasterstatlist") 172*4882a593Smuzhiyun 173*4882a593Smuzhiyun def stat_to_float(value): 174*4882a593Smuzhiyun return float(value.strip('% \n\r')) 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun def _append_read_list(v): 177*4882a593Smuzhiyun lock = bb.utils.lockfile(e.data.expand("${TOPDIR}/toaster.lock"), False, True) 178*4882a593Smuzhiyun 179*4882a593Smuzhiyun with open(toaster_statlist_file, "a") as fout: 180*4882a593Smuzhiyun taskdir = e.data.expand("${BUILDSTATS_BASE}/${BUILDNAME}/${PF}") 181*4882a593Smuzhiyun fout.write("%s::%s::%s::%s\n" % (e.taskfile, e.taskname, os.path.join(taskdir, e.task), e.data.expand("${PN}"))) 182*4882a593Smuzhiyun 183*4882a593Smuzhiyun bb.utils.unlockfile(lock) 184*4882a593Smuzhiyun 185*4882a593Smuzhiyun def _read_stats(filename): 186*4882a593Smuzhiyun # seconds 187*4882a593Smuzhiyun cpu_time_user = 0 188*4882a593Smuzhiyun cpu_time_system = 0 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun # bytes 191*4882a593Smuzhiyun disk_io_read = 0 192*4882a593Smuzhiyun disk_io_write = 0 193*4882a593Smuzhiyun 194*4882a593Smuzhiyun started = 0 195*4882a593Smuzhiyun ended = 0 196*4882a593Smuzhiyun 197*4882a593Smuzhiyun taskname = '' 198*4882a593Smuzhiyun 199*4882a593Smuzhiyun statinfo = {} 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun with open(filename, 'r') as task_bs: 202*4882a593Smuzhiyun for line in task_bs.readlines(): 203*4882a593Smuzhiyun k,v = line.strip().split(": ", 1) 204*4882a593Smuzhiyun statinfo[k] = v 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun if "Started" in statinfo: 207*4882a593Smuzhiyun started = stat_to_float(statinfo["Started"]) 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun if "Ended" in statinfo: 210*4882a593Smuzhiyun ended = stat_to_float(statinfo["Ended"]) 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun if "Child rusage ru_utime" in statinfo: 213*4882a593Smuzhiyun cpu_time_user = cpu_time_user + stat_to_float(statinfo["Child rusage ru_utime"]) 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun if "Child rusage ru_stime" in statinfo: 216*4882a593Smuzhiyun cpu_time_system = cpu_time_system + stat_to_float(statinfo["Child rusage ru_stime"]) 217*4882a593Smuzhiyun 218*4882a593Smuzhiyun if "IO write_bytes" in statinfo: 219*4882a593Smuzhiyun write_bytes = int(statinfo["IO write_bytes"].strip('% \n\r')) 220*4882a593Smuzhiyun disk_io_write = disk_io_write + write_bytes 221*4882a593Smuzhiyun 222*4882a593Smuzhiyun if "IO read_bytes" in statinfo: 223*4882a593Smuzhiyun read_bytes = int(statinfo["IO read_bytes"].strip('% \n\r')) 224*4882a593Smuzhiyun disk_io_read = disk_io_read + read_bytes 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun return { 227*4882a593Smuzhiyun 'stat_file': filename, 228*4882a593Smuzhiyun 'cpu_time_user': cpu_time_user, 229*4882a593Smuzhiyun 'cpu_time_system': cpu_time_system, 230*4882a593Smuzhiyun 'disk_io_read': disk_io_read, 231*4882a593Smuzhiyun 'disk_io_write': disk_io_write, 232*4882a593Smuzhiyun 'started': started, 233*4882a593Smuzhiyun 'ended': ended 234*4882a593Smuzhiyun } 235*4882a593Smuzhiyun 236*4882a593Smuzhiyun if isinstance(e, (bb.build.TaskSucceeded, bb.build.TaskFailed)): 237*4882a593Smuzhiyun _append_read_list(e) 238*4882a593Smuzhiyun pass 239*4882a593Smuzhiyun 240*4882a593Smuzhiyun if isinstance(e, bb.event.BuildCompleted) and os.path.exists(toaster_statlist_file): 241*4882a593Smuzhiyun events = [] 242*4882a593Smuzhiyun with open(toaster_statlist_file, "r") as fin: 243*4882a593Smuzhiyun for line in fin: 244*4882a593Smuzhiyun (taskfile, taskname, filename, recipename) = line.strip().split("::") 245*4882a593Smuzhiyun stats = _read_stats(filename) 246*4882a593Smuzhiyun events.append((taskfile, taskname, stats, recipename)) 247*4882a593Smuzhiyun bb.event.fire(bb.event.MetadataEvent("BuildStatsList", events), e.data) 248*4882a593Smuzhiyun os.unlink(toaster_statlist_file) 249*4882a593Smuzhiyun} 250*4882a593Smuzhiyun 251*4882a593Smuzhiyun# dump relevant build history data as an event when the build is completed 252*4882a593Smuzhiyun 253*4882a593Smuzhiyunpython toaster_buildhistory_dump() { 254*4882a593Smuzhiyun import re 255*4882a593Smuzhiyun BUILDHISTORY_DIR = e.data.expand("${TOPDIR}/buildhistory") 256*4882a593Smuzhiyun BUILDHISTORY_DIR_IMAGE_BASE = e.data.expand("%s/images/${MACHINE_ARCH}/${TCLIBC}/"% BUILDHISTORY_DIR) 257*4882a593Smuzhiyun pkgdata_dir = e.data.getVar("PKGDATA_DIR") 258*4882a593Smuzhiyun 259*4882a593Smuzhiyun 260*4882a593Smuzhiyun # scan the build targets for this build 261*4882a593Smuzhiyun images = {} 262*4882a593Smuzhiyun allpkgs = {} 263*4882a593Smuzhiyun files = {} 264*4882a593Smuzhiyun for target in e._pkgs: 265*4882a593Smuzhiyun target = target.split(':')[0] # strip ':<task>' suffix from the target 266*4882a593Smuzhiyun installed_img_path = e.data.expand(os.path.join(BUILDHISTORY_DIR_IMAGE_BASE, target)) 267*4882a593Smuzhiyun if os.path.exists(installed_img_path): 268*4882a593Smuzhiyun images[target] = {} 269*4882a593Smuzhiyun files[target] = {} 270*4882a593Smuzhiyun files[target]['dirs'] = [] 271*4882a593Smuzhiyun files[target]['syms'] = [] 272*4882a593Smuzhiyun files[target]['files'] = [] 273*4882a593Smuzhiyun with open("%s/installed-package-sizes.txt" % installed_img_path, "r") as fin: 274*4882a593Smuzhiyun for line in fin: 275*4882a593Smuzhiyun line = line.rstrip(";") 276*4882a593Smuzhiyun psize, punit, pname = line.split() 277*4882a593Smuzhiyun # this size is "installed-size" as it measures how much space it takes on disk 278*4882a593Smuzhiyun images[target][pname.strip()] = {'size':int(psize)*1024, 'depends' : []} 279*4882a593Smuzhiyun 280*4882a593Smuzhiyun with open("%s/depends.dot" % installed_img_path, "r") as fin: 281*4882a593Smuzhiyun p = re.compile(r'\s*"(?P<name>[^"]+)"\s*->\s*"(?P<dep>[^"]+)"(?P<rec>.*?\[style=dotted\])?') 282*4882a593Smuzhiyun for line in fin: 283*4882a593Smuzhiyun m = p.match(line) 284*4882a593Smuzhiyun if not m: 285*4882a593Smuzhiyun continue 286*4882a593Smuzhiyun pname = m.group('name') 287*4882a593Smuzhiyun dependsname = m.group('dep') 288*4882a593Smuzhiyun deptype = 'recommends' if m.group('rec') else 'depends' 289*4882a593Smuzhiyun 290*4882a593Smuzhiyun # If RPM is used for packaging, then there may be 291*4882a593Smuzhiyun # dependencies such as "/bin/sh", which will confuse 292*4882a593Smuzhiyun # _toaster_load_pkgdatafile() later on. While at it, ignore 293*4882a593Smuzhiyun # any dependencies that contain parentheses, e.g., 294*4882a593Smuzhiyun # "libc.so.6(GLIBC_2.7)". 295*4882a593Smuzhiyun if dependsname.startswith('/') or '(' in dependsname: 296*4882a593Smuzhiyun continue 297*4882a593Smuzhiyun 298*4882a593Smuzhiyun if not pname in images[target]: 299*4882a593Smuzhiyun images[target][pname] = {'size': 0, 'depends' : []} 300*4882a593Smuzhiyun if not dependsname in images[target]: 301*4882a593Smuzhiyun images[target][dependsname] = {'size': 0, 'depends' : []} 302*4882a593Smuzhiyun images[target][pname]['depends'].append((dependsname, deptype)) 303*4882a593Smuzhiyun 304*4882a593Smuzhiyun # files-in-image.txt is only generated if an image file is created, 305*4882a593Smuzhiyun # so the file entries ('syms', 'dirs', 'files') for a target will be 306*4882a593Smuzhiyun # empty for rootfs builds and other "image" tasks which don't 307*4882a593Smuzhiyun # produce image files 308*4882a593Smuzhiyun # (e.g. "bitbake core-image-minimal -c populate_sdk") 309*4882a593Smuzhiyun files_in_image_path = "%s/files-in-image.txt" % installed_img_path 310*4882a593Smuzhiyun if os.path.exists(files_in_image_path): 311*4882a593Smuzhiyun with open(files_in_image_path, "r") as fin: 312*4882a593Smuzhiyun for line in fin: 313*4882a593Smuzhiyun lc = [ x for x in line.strip().split(" ") if len(x) > 0 ] 314*4882a593Smuzhiyun if lc[0].startswith("l"): 315*4882a593Smuzhiyun files[target]['syms'].append(lc) 316*4882a593Smuzhiyun elif lc[0].startswith("d"): 317*4882a593Smuzhiyun files[target]['dirs'].append(lc) 318*4882a593Smuzhiyun else: 319*4882a593Smuzhiyun files[target]['files'].append(lc) 320*4882a593Smuzhiyun 321*4882a593Smuzhiyun for pname in images[target]: 322*4882a593Smuzhiyun if not pname in allpkgs: 323*4882a593Smuzhiyun try: 324*4882a593Smuzhiyun pkgdata = _toaster_load_pkgdatafile("%s/runtime-reverse/" % pkgdata_dir, pname) 325*4882a593Smuzhiyun except IOError as err: 326*4882a593Smuzhiyun if err.errno == 2: 327*4882a593Smuzhiyun # We expect this e.g. for RRECOMMENDS that are unsatisfied at runtime 328*4882a593Smuzhiyun continue 329*4882a593Smuzhiyun else: 330*4882a593Smuzhiyun raise 331*4882a593Smuzhiyun allpkgs[pname] = pkgdata 332*4882a593Smuzhiyun 333*4882a593Smuzhiyun 334*4882a593Smuzhiyun data = { 'pkgdata' : allpkgs, 'imgdata' : images, 'filedata' : files } 335*4882a593Smuzhiyun 336*4882a593Smuzhiyun bb.event.fire(bb.event.MetadataEvent("ImagePkgList", data), e.data) 337*4882a593Smuzhiyun 338*4882a593Smuzhiyun} 339*4882a593Smuzhiyun 340*4882a593Smuzhiyun# get list of artifacts from sstate manifest 341*4882a593Smuzhiyunpython toaster_artifacts() { 342*4882a593Smuzhiyun if e.taskname in ["do_deploy", "do_image_complete", "do_populate_sdk", "do_populate_sdk_ext"]: 343*4882a593Smuzhiyun d2 = d.createCopy() 344*4882a593Smuzhiyun d2.setVar('FILE', e.taskfile) 345*4882a593Smuzhiyun # Use 'stamp-extra-info' if present, else use workaround 346*4882a593Smuzhiyun # to determine 'SSTATE_MANMACH' 347*4882a593Smuzhiyun extrainf = d2.getVarFlag(e.taskname, 'stamp-extra-info') 348*4882a593Smuzhiyun if extrainf: 349*4882a593Smuzhiyun d2.setVar('SSTATE_MANMACH', extrainf) 350*4882a593Smuzhiyun else: 351*4882a593Smuzhiyun if "do_populate_sdk" == e.taskname: 352*4882a593Smuzhiyun d2.setVar('SSTATE_MANMACH', d2.expand("${MACHINE}${SDKMACHINE}")) 353*4882a593Smuzhiyun else: 354*4882a593Smuzhiyun d2.setVar('SSTATE_MANMACH', d2.expand("${MACHINE}")) 355*4882a593Smuzhiyun manifest = oe.sstatesig.sstate_get_manifest_filename(e.taskname[3:], d2)[0] 356*4882a593Smuzhiyun 357*4882a593Smuzhiyun if os.access(manifest, os.R_OK): 358*4882a593Smuzhiyun with open(manifest) as fmanifest: 359*4882a593Smuzhiyun artifacts = [fname.strip() for fname in fmanifest] 360*4882a593Smuzhiyun data = {"task": e.taskid, "artifacts": artifacts} 361*4882a593Smuzhiyun bb.event.fire(bb.event.MetadataEvent("TaskArtifacts", data), d2) 362*4882a593Smuzhiyun} 363*4882a593Smuzhiyun 364*4882a593Smuzhiyun# set event handlers 365*4882a593Smuzhiyunaddhandler toaster_layerinfo_dumpdata 366*4882a593Smuzhiyuntoaster_layerinfo_dumpdata[eventmask] = "bb.event.TreeDataPreparationCompleted" 367*4882a593Smuzhiyun 368*4882a593Smuzhiyunaddhandler toaster_collect_task_stats 369*4882a593Smuzhiyuntoaster_collect_task_stats[eventmask] = "bb.event.BuildCompleted bb.build.TaskSucceeded bb.build.TaskFailed" 370*4882a593Smuzhiyun 371*4882a593Smuzhiyunaddhandler toaster_buildhistory_dump 372*4882a593Smuzhiyuntoaster_buildhistory_dump[eventmask] = "bb.event.BuildCompleted" 373*4882a593Smuzhiyun 374*4882a593Smuzhiyunaddhandler toaster_artifacts 375*4882a593Smuzhiyuntoaster_artifacts[eventmask] = "bb.runqueue.runQueueTaskSkipped bb.runqueue.runQueueTaskCompleted" 376*4882a593Smuzhiyun 377*4882a593Smuzhiyundo_packagedata_setscene[postfuncs] += "toaster_packagedata_dumpdata " 378*4882a593Smuzhiyundo_packagedata_setscene[vardepsexclude] += "toaster_packagedata_dumpdata " 379*4882a593Smuzhiyun 380*4882a593Smuzhiyundo_package[postfuncs] += "toaster_package_dumpdata " 381*4882a593Smuzhiyundo_package[vardepsexclude] += "toaster_package_dumpdata " 382*4882a593Smuzhiyun 383*4882a593Smuzhiyun#do_populate_sdk[postfuncs] += "toaster_artifact_dumpdata " 384*4882a593Smuzhiyun#do_populate_sdk[vardepsexclude] += "toaster_artifact_dumpdata " 385*4882a593Smuzhiyun 386*4882a593Smuzhiyun#do_populate_sdk_ext[postfuncs] += "toaster_artifact_dumpdata " 387*4882a593Smuzhiyun#do_populate_sdk_ext[vardepsexclude] += "toaster_artifact_dumpdata " 388*4882a593Smuzhiyun 389