diff options
author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-11-02 09:02:15 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-11-03 10:12:42 +0000 |
commit | 34e4eebc32c4836fc40098e90b17c00f51398967 (patch) | |
tree | 20f3ba43c0341cbed8b454c92d60e0fc326b9d50 | |
parent | 791d6e63be09b361dd3397707a0507399b9a9ce7 (diff) | |
download | poky-34e4eebc32c4836fc40098e90b17c00f51398967.tar.gz |
bitbake: lib/bb: Fix string concatination potential performance issues
Python scales badly when concatinating strings in loops. Most of these
references aren't problematic but at least one (in data.py) is probably
a performance issue as the issue is compounded as strings become large.
The way to handle this in python is to create lists which don't reconstruct
all the objects when appending to them. We may as well fix all the references
since it stops them being copy/pasted into something problematic in the future.
This patch was based on issues highligthted by a report from AWS Codeguru.
(Bitbake rev: d654139a833127b16274dca0ccbbab7e3bb33ed0)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | bitbake/lib/bb/data.py | 24 | ||||
-rw-r--r-- | bitbake/lib/bb/data_smart.py | 6 | ||||
-rw-r--r-- | bitbake/lib/bb/fetch2/__init__.py | 16 | ||||
-rw-r--r-- | bitbake/lib/bb/runqueue.py | 37 | ||||
-rw-r--r-- | bitbake/lib/bb/server/process.py | 6 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/buildinfohelper.py | 16 |
6 files changed, 53 insertions, 52 deletions
diff --git a/bitbake/lib/bb/data.py b/bitbake/lib/bb/data.py index 9d18b1e2bf..ee5557abfa 100644 --- a/bitbake/lib/bb/data.py +++ b/bitbake/lib/bb/data.py | |||
@@ -285,21 +285,19 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d): | |||
285 | vardeps = varflags.get("vardeps") | 285 | vardeps = varflags.get("vardeps") |
286 | 286 | ||
287 | def handle_contains(value, contains, d): | 287 | def handle_contains(value, contains, d): |
288 | newvalue = "" | 288 | newvalue = [] |
289 | if value: | ||
290 | newvalue.append(str(value)) | ||
289 | for k in sorted(contains): | 291 | for k in sorted(contains): |
290 | l = (d.getVar(k) or "").split() | 292 | l = (d.getVar(k) or "").split() |
291 | for item in sorted(contains[k]): | 293 | for item in sorted(contains[k]): |
292 | for word in item.split(): | 294 | for word in item.split(): |
293 | if not word in l: | 295 | if not word in l: |
294 | newvalue += "\n%s{%s} = Unset" % (k, item) | 296 | newvalue.append("\n%s{%s} = Unset" % (k, item)) |
295 | break | 297 | break |
296 | else: | 298 | else: |
297 | newvalue += "\n%s{%s} = Set" % (k, item) | 299 | newvalue.append("\n%s{%s} = Set" % (k, item)) |
298 | if not newvalue: | 300 | return "".join(newvalue) |
299 | return value | ||
300 | if not value: | ||
301 | return newvalue | ||
302 | return value + newvalue | ||
303 | 301 | ||
304 | def handle_remove(value, deps, removes, d): | 302 | def handle_remove(value, deps, removes, d): |
305 | for r in sorted(removes): | 303 | for r in sorted(removes): |
@@ -406,7 +404,9 @@ def generate_dependency_hash(tasklist, gendeps, lookupcache, whitelist, fn): | |||
406 | 404 | ||
407 | if data is None: | 405 | if data is None: |
408 | bb.error("Task %s from %s seems to be empty?!" % (task, fn)) | 406 | bb.error("Task %s from %s seems to be empty?!" % (task, fn)) |
409 | data = '' | 407 | data = [] |
408 | else: | ||
409 | data = [data] | ||
410 | 410 | ||
411 | gendeps[task] -= whitelist | 411 | gendeps[task] -= whitelist |
412 | newdeps = gendeps[task] | 412 | newdeps = gendeps[task] |
@@ -424,12 +424,12 @@ def generate_dependency_hash(tasklist, gendeps, lookupcache, whitelist, fn): | |||
424 | 424 | ||
425 | alldeps = sorted(seen) | 425 | alldeps = sorted(seen) |
426 | for dep in alldeps: | 426 | for dep in alldeps: |
427 | data = data + dep | 427 | data.append(dep) |
428 | var = lookupcache[dep] | 428 | var = lookupcache[dep] |
429 | if var is not None: | 429 | if var is not None: |
430 | data = data + str(var) | 430 | data.append(str(var)) |
431 | k = fn + ":" + task | 431 | k = fn + ":" + task |
432 | basehash[k] = hashlib.sha256(data.encode("utf-8")).hexdigest() | 432 | basehash[k] = hashlib.sha256("".join(data).encode("utf-8")).hexdigest() |
433 | taskdeps[task] = alldeps | 433 | taskdeps[task] = alldeps |
434 | 434 | ||
435 | return taskdeps, basehash | 435 | return taskdeps, basehash |
diff --git a/bitbake/lib/bb/data_smart.py b/bitbake/lib/bb/data_smart.py index 8d235da121..7ed7112bdc 100644 --- a/bitbake/lib/bb/data_smart.py +++ b/bitbake/lib/bb/data_smart.py | |||
@@ -810,7 +810,7 @@ class DataSmart(MutableMapping): | |||
810 | expanded_removes[r] = self.expand(r).split() | 810 | expanded_removes[r] = self.expand(r).split() |
811 | 811 | ||
812 | parser.removes = set() | 812 | parser.removes = set() |
813 | val = "" | 813 | val = [] |
814 | for v in __whitespace_split__.split(parser.value): | 814 | for v in __whitespace_split__.split(parser.value): |
815 | skip = False | 815 | skip = False |
816 | for r in removes: | 816 | for r in removes: |
@@ -819,8 +819,8 @@ class DataSmart(MutableMapping): | |||
819 | skip = True | 819 | skip = True |
820 | if skip: | 820 | if skip: |
821 | continue | 821 | continue |
822 | val = val + v | 822 | val.append(v) |
823 | parser.value = val | 823 | parser.value = "".join(val) |
824 | if expand: | 824 | if expand: |
825 | value = parser.value | 825 | value = parser.value |
826 | 826 | ||
diff --git a/bitbake/lib/bb/fetch2/__init__.py b/bitbake/lib/bb/fetch2/__init__.py index 1d6e4e0964..43b312ce7e 100644 --- a/bitbake/lib/bb/fetch2/__init__.py +++ b/bitbake/lib/bb/fetch2/__init__.py | |||
@@ -402,24 +402,24 @@ def encodeurl(decoded): | |||
402 | 402 | ||
403 | if not type: | 403 | if not type: |
404 | raise MissingParameterError('type', "encoded from the data %s" % str(decoded)) | 404 | raise MissingParameterError('type', "encoded from the data %s" % str(decoded)) |
405 | url = '%s://' % type | 405 | url = ['%s://' % type] |
406 | if user and type != "file": | 406 | if user and type != "file": |
407 | url += "%s" % user | 407 | url.append("%s" % user) |
408 | if pswd: | 408 | if pswd: |
409 | url += ":%s" % pswd | 409 | url.append(":%s" % pswd) |
410 | url += "@" | 410 | url.append("@") |
411 | if host and type != "file": | 411 | if host and type != "file": |
412 | url += "%s" % host | 412 | url.append("%s" % host) |
413 | if path: | 413 | if path: |
414 | # Standardise path to ensure comparisons work | 414 | # Standardise path to ensure comparisons work |
415 | while '//' in path: | 415 | while '//' in path: |
416 | path = path.replace("//", "/") | 416 | path = path.replace("//", "/") |
417 | url += "%s" % urllib.parse.quote(path) | 417 | url.append("%s" % urllib.parse.quote(path)) |
418 | if p: | 418 | if p: |
419 | for parm in p: | 419 | for parm in p: |
420 | url += ";%s=%s" % (parm, p[parm]) | 420 | url.append(";%s=%s" % (parm, p[parm])) |
421 | 421 | ||
422 | return url | 422 | return "".join(url) |
423 | 423 | ||
424 | def uri_replace(ud, uri_find, uri_replace, replacements, d, mirrortarball=None): | 424 | def uri_replace(ud, uri_find, uri_replace, replacements, d, mirrortarball=None): |
425 | if not ud.url or not uri_find or not uri_replace: | 425 | if not ud.url or not uri_find or not uri_replace: |
diff --git a/bitbake/lib/bb/runqueue.py b/bitbake/lib/bb/runqueue.py index 87c00462c1..774cdbca0b 100644 --- a/bitbake/lib/bb/runqueue.py +++ b/bitbake/lib/bb/runqueue.py | |||
@@ -1061,12 +1061,12 @@ class RunQueueData: | |||
1061 | seen_pn.append(pn) | 1061 | seen_pn.append(pn) |
1062 | else: | 1062 | else: |
1063 | bb.fatal("Multiple versions of %s are due to be built (%s). Only one version of a given PN should be built in any given build. You likely need to set PREFERRED_VERSION_%s to select the correct version or don't depend on multiple versions." % (pn, " ".join(prov_list[prov]), pn)) | 1063 | bb.fatal("Multiple versions of %s are due to be built (%s). Only one version of a given PN should be built in any given build. You likely need to set PREFERRED_VERSION_%s to select the correct version or don't depend on multiple versions." % (pn, " ".join(prov_list[prov]), pn)) |
1064 | msg = "Multiple .bb files are due to be built which each provide %s:\n %s" % (prov, "\n ".join(prov_list[prov])) | 1064 | msgs = ["Multiple .bb files are due to be built which each provide %s:\n %s" % (prov, "\n ".join(prov_list[prov]))] |
1065 | # | 1065 | # |
1066 | # Construct a list of things which uniquely depend on each provider | 1066 | # Construct a list of things which uniquely depend on each provider |
1067 | # since this may help the user figure out which dependency is triggering this warning | 1067 | # since this may help the user figure out which dependency is triggering this warning |
1068 | # | 1068 | # |
1069 | msg += "\nA list of tasks depending on these providers is shown and may help explain where the dependency comes from." | 1069 | msgs.append("\nA list of tasks depending on these providers is shown and may help explain where the dependency comes from.") |
1070 | deplist = {} | 1070 | deplist = {} |
1071 | commondeps = None | 1071 | commondeps = None |
1072 | for provfn in prov_list[prov]: | 1072 | for provfn in prov_list[prov]: |
@@ -1086,12 +1086,12 @@ class RunQueueData: | |||
1086 | commondeps &= deps | 1086 | commondeps &= deps |
1087 | deplist[provfn] = deps | 1087 | deplist[provfn] = deps |
1088 | for provfn in deplist: | 1088 | for provfn in deplist: |
1089 | msg += "\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps)) | 1089 | msgs.append("\n%s has unique dependees:\n %s" % (provfn, "\n ".join(deplist[provfn] - commondeps))) |
1090 | # | 1090 | # |
1091 | # Construct a list of provides and runtime providers for each recipe | 1091 | # Construct a list of provides and runtime providers for each recipe |
1092 | # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC) | 1092 | # (rprovides has to cover RPROVIDES, PACKAGES, PACKAGES_DYNAMIC) |
1093 | # | 1093 | # |
1094 | msg += "\nIt could be that one recipe provides something the other doesn't and should. The following provider and runtime provider differences may be helpful." | 1094 | msgs.append("\nIt could be that one recipe provides something the other doesn't and should. The following provider and runtime provider differences may be helpful.") |
1095 | provide_results = {} | 1095 | provide_results = {} |
1096 | rprovide_results = {} | 1096 | rprovide_results = {} |
1097 | commonprovs = None | 1097 | commonprovs = None |
@@ -1118,16 +1118,16 @@ class RunQueueData: | |||
1118 | else: | 1118 | else: |
1119 | commonrprovs &= rprovides | 1119 | commonrprovs &= rprovides |
1120 | rprovide_results[provfn] = rprovides | 1120 | rprovide_results[provfn] = rprovides |
1121 | #msg += "\nCommon provides:\n %s" % ("\n ".join(commonprovs)) | 1121 | #msgs.append("\nCommon provides:\n %s" % ("\n ".join(commonprovs))) |
1122 | #msg += "\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs)) | 1122 | #msgs.append("\nCommon rprovides:\n %s" % ("\n ".join(commonrprovs))) |
1123 | for provfn in prov_list[prov]: | 1123 | for provfn in prov_list[prov]: |
1124 | msg += "\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs)) | 1124 | msgs.append("\n%s has unique provides:\n %s" % (provfn, "\n ".join(provide_results[provfn] - commonprovs))) |
1125 | msg += "\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs)) | 1125 | msgs.append("\n%s has unique rprovides:\n %s" % (provfn, "\n ".join(rprovide_results[provfn] - commonrprovs))) |
1126 | 1126 | ||
1127 | if self.warn_multi_bb: | 1127 | if self.warn_multi_bb: |
1128 | logger.verbnote(msg) | 1128 | logger.verbnote("".join(msgs)) |
1129 | else: | 1129 | else: |
1130 | logger.error(msg) | 1130 | logger.error("".join(msgs)) |
1131 | 1131 | ||
1132 | self.init_progress_reporter.next_stage() | 1132 | self.init_progress_reporter.next_stage() |
1133 | 1133 | ||
@@ -1935,7 +1935,7 @@ class RunQueueExecute: | |||
1935 | self.stats.taskFailed() | 1935 | self.stats.taskFailed() |
1936 | self.failed_tids.append(task) | 1936 | self.failed_tids.append(task) |
1937 | 1937 | ||
1938 | fakeroot_log = "" | 1938 | fakeroot_log = [] |
1939 | if fakerootlog and os.path.exists(fakerootlog): | 1939 | if fakerootlog and os.path.exists(fakerootlog): |
1940 | with open(fakerootlog) as fakeroot_log_file: | 1940 | with open(fakerootlog) as fakeroot_log_file: |
1941 | fakeroot_failed = False | 1941 | fakeroot_failed = False |
@@ -1945,12 +1945,12 @@ class RunQueueExecute: | |||
1945 | fakeroot_failed = True | 1945 | fakeroot_failed = True |
1946 | if 'doing new pid setup and server start' in line: | 1946 | if 'doing new pid setup and server start' in line: |
1947 | break | 1947 | break |
1948 | fakeroot_log = line + fakeroot_log | 1948 | fakeroot_log.append(line) |
1949 | 1949 | ||
1950 | if not fakeroot_failed: | 1950 | if not fakeroot_failed: |
1951 | fakeroot_log = None | 1951 | fakeroot_log = [] |
1952 | 1952 | ||
1953 | bb.event.fire(runQueueTaskFailed(task, self.stats, exitcode, self.rq, fakeroot_log=fakeroot_log), self.cfgData) | 1953 | bb.event.fire(runQueueTaskFailed(task, self.stats, exitcode, self.rq, fakeroot_log=("".join(fakeroot_log) or None)), self.cfgData) |
1954 | 1954 | ||
1955 | if self.rqdata.taskData[''].abort: | 1955 | if self.rqdata.taskData[''].abort: |
1956 | self.rq.state = runQueueCleanUp | 1956 | self.rq.state = runQueueCleanUp |
@@ -2608,12 +2608,13 @@ class RunQueueExecute: | |||
2608 | pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn] | 2608 | pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn] |
2609 | if not check_setscene_enforce_whitelist(pn, taskname, self.rqdata.setscenewhitelist): | 2609 | if not check_setscene_enforce_whitelist(pn, taskname, self.rqdata.setscenewhitelist): |
2610 | if tid in self.rqdata.runq_setscene_tids: | 2610 | if tid in self.rqdata.runq_setscene_tids: |
2611 | msg = 'Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname) | 2611 | msg = ['Task %s.%s attempted to execute unexpectedly and should have been setscened' % (pn, taskname)] |
2612 | else: | 2612 | else: |
2613 | msg = 'Task %s.%s attempted to execute unexpectedly' % (pn, taskname) | 2613 | msg = ['Task %s.%s attempted to execute unexpectedly' % (pn, taskname)] |
2614 | for t in self.scenequeue_notcovered: | 2614 | for t in self.scenequeue_notcovered: |
2615 | msg = msg + "\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash) | 2615 | msg.append("\nTask %s, unihash %s, taskhash %s" % (t, self.rqdata.runtaskentries[t].unihash, self.rqdata.runtaskentries[t].hash)) |
2616 | logger.error(msg + '\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered)) | 2616 | msg.append('\nThis is usually due to missing setscene tasks. Those missing in this build were: %s' % pprint.pformat(self.scenequeue_notcovered)) |
2617 | logger.error("".join(msg)) | ||
2617 | return True | 2618 | return True |
2618 | return False | 2619 | return False |
2619 | 2620 | ||
diff --git a/bitbake/lib/bb/server/process.py b/bitbake/lib/bb/server/process.py index 8fdcc66dc7..1636616660 100644 --- a/bitbake/lib/bb/server/process.py +++ b/bitbake/lib/bb/server/process.py | |||
@@ -326,10 +326,10 @@ class ProcessServer(): | |||
326 | if e.errno != errno.ENOENT: | 326 | if e.errno != errno.ENOENT: |
327 | raise | 327 | raise |
328 | 328 | ||
329 | msg = "Delaying shutdown due to active processes which appear to be holding bitbake.lock" | 329 | msg = ["Delaying shutdown due to active processes which appear to be holding bitbake.lock"] |
330 | if procs: | 330 | if procs: |
331 | msg += ":\n%s" % str(procs.decode("utf-8")) | 331 | msg.append(":\n%s" % str(procs.decode("utf-8"))) |
332 | serverlog(msg) | 332 | serverlog("".join(msg)) |
333 | 333 | ||
334 | def idle_commands(self, delay, fds=None): | 334 | def idle_commands(self, delay, fds=None): |
335 | nextsleep = delay | 335 | nextsleep = delay |
diff --git a/bitbake/lib/bb/ui/buildinfohelper.py b/bitbake/lib/bb/ui/buildinfohelper.py index 8588849dd4..835e92c299 100644 --- a/bitbake/lib/bb/ui/buildinfohelper.py +++ b/bitbake/lib/bb/ui/buildinfohelper.py | |||
@@ -571,7 +571,7 @@ class ORMWrapper(object): | |||
571 | assert isinstance(build_obj, Build) | 571 | assert isinstance(build_obj, Build) |
572 | assert isinstance(target_obj, Target) | 572 | assert isinstance(target_obj, Target) |
573 | 573 | ||
574 | errormsg = "" | 574 | errormsg = [] |
575 | for p in packagedict: | 575 | for p in packagedict: |
576 | # Search name swtiches round the installed name vs package name | 576 | # Search name swtiches round the installed name vs package name |
577 | # by default installed name == package name | 577 | # by default installed name == package name |
@@ -636,7 +636,7 @@ class ORMWrapper(object): | |||
636 | if packagefile_objects: | 636 | if packagefile_objects: |
637 | Package_File.objects.bulk_create(packagefile_objects) | 637 | Package_File.objects.bulk_create(packagefile_objects) |
638 | except KeyError as e: | 638 | except KeyError as e: |
639 | errormsg += " stpi: Key error, package %s key %s \n" % ( p, e ) | 639 | errormsg.append(" stpi: Key error, package %s key %s \n" % (p, e)) |
640 | 640 | ||
641 | # save disk installed size | 641 | # save disk installed size |
642 | packagedict[p]['object'].installed_size = packagedict[p]['size'] | 642 | packagedict[p]['object'].installed_size = packagedict[p]['size'] |
@@ -678,8 +678,8 @@ class ORMWrapper(object): | |||
678 | else: | 678 | else: |
679 | logger.info("No package dependencies created") | 679 | logger.info("No package dependencies created") |
680 | 680 | ||
681 | if len(errormsg) > 0: | 681 | if errormsg: |
682 | logger.warning("buildinfohelper: target_package_info could not identify recipes: \n%s", errormsg) | 682 | logger.warning("buildinfohelper: target_package_info could not identify recipes: \n%s", "".join(errormsg)) |
683 | 683 | ||
684 | def save_target_image_file_information(self, target_obj, file_name, file_size): | 684 | def save_target_image_file_information(self, target_obj, file_name, file_size): |
685 | Target_Image_File.objects.create(target=target_obj, | 685 | Target_Image_File.objects.create(target=target_obj, |
@@ -1404,7 +1404,7 @@ class BuildInfoHelper(object): | |||
1404 | assert 'pn' in event._depgraph | 1404 | assert 'pn' in event._depgraph |
1405 | assert 'tdepends' in event._depgraph | 1405 | assert 'tdepends' in event._depgraph |
1406 | 1406 | ||
1407 | errormsg = "" | 1407 | errormsg = [] |
1408 | 1408 | ||
1409 | # save layer version priorities | 1409 | # save layer version priorities |
1410 | if 'layer-priorities' in event._depgraph.keys(): | 1410 | if 'layer-priorities' in event._depgraph.keys(): |
@@ -1496,7 +1496,7 @@ class BuildInfoHelper(object): | |||
1496 | elif dep in self.internal_state['recipes']: | 1496 | elif dep in self.internal_state['recipes']: |
1497 | dependency = self.internal_state['recipes'][dep] | 1497 | dependency = self.internal_state['recipes'][dep] |
1498 | else: | 1498 | else: |
1499 | errormsg += " stpd: KeyError saving recipe dependency for %s, %s \n" % (recipe, dep) | 1499 | errormsg.append(" stpd: KeyError saving recipe dependency for %s, %s \n" % (recipe, dep)) |
1500 | continue | 1500 | continue |
1501 | recipe_dep = Recipe_Dependency(recipe=target, | 1501 | recipe_dep = Recipe_Dependency(recipe=target, |
1502 | depends_on=dependency, | 1502 | depends_on=dependency, |
@@ -1537,8 +1537,8 @@ class BuildInfoHelper(object): | |||
1537 | taskdeps_objects.append(Task_Dependency( task = target, depends_on = dep )) | 1537 | taskdeps_objects.append(Task_Dependency( task = target, depends_on = dep )) |
1538 | Task_Dependency.objects.bulk_create(taskdeps_objects) | 1538 | Task_Dependency.objects.bulk_create(taskdeps_objects) |
1539 | 1539 | ||
1540 | if len(errormsg) > 0: | 1540 | if errormsg: |
1541 | logger.warning("buildinfohelper: dependency info not identify recipes: \n%s", errormsg) | 1541 | logger.warning("buildinfohelper: dependency info not identify recipes: \n%s", "".join(errormsg)) |
1542 | 1542 | ||
1543 | 1543 | ||
1544 | def store_build_package_information(self, event): | 1544 | def store_build_package_information(self, event): |