diff options
author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2020-08-06 15:57:25 -0400 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2020-08-08 09:17:49 +0100 |
commit | 24f830fc75f46f4c9e87a757713e09cf91d76a73 (patch) | |
tree | 5b410a5c59fd0a619ef0c6354098b5f045ad9775 /meta/classes | |
parent | bfabdcfa3786f6b7c8d4df5130f653df299d3ab6 (diff) | |
download | poky-24f830fc75f46f4c9e87a757713e09cf91d76a73.tar.gz |
kernel-yocto: enhance configuration queue analysis capabilities
Enable the kernel-yocto bbclass to use enhanced capabilities from
the kern-tools symbol_why.pl.
We bump the kern-tools SRCREV to pickup the reworking of symbol_why,
which uses Kconfiglib to provide analysis on configuration values.
This is useful for debugging why a symbol specified in a fragment
did not end up in the final .config.
We introduce two ways to interact with the new symbol_why:
1) a replacement of the existing kconf_check script
2) a dedicated task that is explicitly invoked to dump details
on the configuration.
The kconf_check replacement is transparent to the user, and is
run in exactly the same way as it was previously. But we get better
output and more detailed diagnostics if there are symbols that
don't make it into the final .config
The second way to interact with symbol why is via the new task
do_config_analysis. This is invoked like any other task, and by
default will provide a full configuration analysis and point the
user at files to look at for details.
If a more targetted analysis is desired, then specific symbols
can be set in the CONFIG_ANALYSIS variable. When this variable
is set, the task will only run for the given symbols and provide
per-variable links to the user. This variable can be set like
any other, including specification in the local.conf:
CONFIG_ANALYSIS_pn-linux-yocto-dev = 'NF_CONNTRACK LOCALVERSION'
Which produces output as follows:
WARNING: linux-yocto-dev-5.8-rc++gitAUTOINC+d22beb8f8a_8fc484ed37-r0
do_config_analysis: Configuration analysis executed, see: tmp/work/qemuarm64-poky-linux/linux-yocto-dev/5.8-rc++gitAUTOINC+d22beb8f8a_8fc484ed37-r0/NF_CONNTRACK-config-analysis.txt for details
WARNING: linux-yocto-dev-5.8-rc++gitAUTOINC+d22beb8f8a_8fc484ed37-r0
do_config_analysis: Configuration audit executed, see: tmp/work/qemuarm64-poky-linux/linux-yocto-dev/5.8-rc++gitAUTOINC+d22beb8f8a_8fc484ed37-r0/NF_CONNTRACK-config-audit.txt for details
WARNING: linux-yocto-dev-5.8-rc++gitAUTOINC+d22beb8f8a_8fc484ed37-r0
do_config_analysis: Configuration analysis executed, see: tmp/work/qemuarm64-poky-linux/linux-yocto-dev/5.8-rc++gitAUTOINC+d22beb8f8a_8fc484ed37-r0/LOCALVERSION-config-analysis.txt for details
WARNING: linux-yocto-dev-5.8-rc++gitAUTOINC+d22beb8f8a_8fc484ed37-r0
do_config_analysis: Configuration audit executed, see: work/qemuarm64-poky-linux/linux-yocto-dev/5.8-rc++gitAUTOINC+d22beb8f8a_8fc484ed37-r0/LOCALVERSION-config-audit.txt for details
(From OE-Core rev: cbc896def4c8bab3150d3405969e5dd018d62d0c)
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/classes')
-rw-r--r-- | meta/classes/kernel-yocto.bbclass | 159 |
1 files changed, 126 insertions, 33 deletions
diff --git a/meta/classes/kernel-yocto.bbclass b/meta/classes/kernel-yocto.bbclass index 70818cc01c..77849a28c9 100644 --- a/meta/classes/kernel-yocto.bbclass +++ b/meta/classes/kernel-yocto.bbclass | |||
@@ -405,6 +405,67 @@ do_kernel_configme() { | |||
405 | } | 405 | } |
406 | 406 | ||
407 | addtask kernel_configme before do_configure after do_patch | 407 | addtask kernel_configme before do_configure after do_patch |
408 | addtask config_analysis | ||
409 | |||
410 | do_config_analysis[depends] = "virtual/kernel:do_configure" | ||
411 | do_config_analysis[depends] += "kern-tools-native:do_populate_sysroot" | ||
412 | |||
413 | CONFIG_AUDIT_FILE ?= "${WORKDIR}/config-audit.txt" | ||
414 | CONFIG_ANALYSIS_FILE ?= "${WORKDIR}/config-analysis.txt" | ||
415 | |||
416 | python do_config_analysis() { | ||
417 | import re, string, sys, subprocess | ||
418 | |||
419 | s = d.getVar('S') | ||
420 | |||
421 | env = os.environ.copy() | ||
422 | env['PATH'] = "%s:%s%s" % (d.getVar('PATH'), s, "/scripts/util/") | ||
423 | env['LD'] = d.getVar('KERNEL_LD') | ||
424 | env['CC'] = d.getVar('KERNEL_CC') | ||
425 | env['ARCH'] = d.getVar('ARCH') | ||
426 | env['srctree'] = s | ||
427 | |||
428 | # read specific symbols from the kernel recipe or from local.conf | ||
429 | # i.e.: CONFIG_ANALYSIS_pn-linux-yocto-dev = 'NF_CONNTRACK LOCALVERSION' | ||
430 | config = d.getVar( 'CONFIG_ANALYSIS' ) | ||
431 | if not config: | ||
432 | config = [ "" ] | ||
433 | else: | ||
434 | config = config.split() | ||
435 | |||
436 | for c in config: | ||
437 | for action in ["analysis","audit"]: | ||
438 | if action == "analysis": | ||
439 | try: | ||
440 | analysis = subprocess.check_output(['symbol_why.py', '--dotconfig', '{}'.format( d.getVar('B') + '/.config' ), '--blame', c], cwd=s, env=env ).decode('utf-8') | ||
441 | except subprocess.CalledProcessError as e: | ||
442 | bb.fatal( "config analysis failed: %s" % e.output.decode('utf-8')) | ||
443 | |||
444 | outfile = d.getVar( 'CONFIG_ANALYSIS_FILE' ) | ||
445 | |||
446 | if action == "audit": | ||
447 | try: | ||
448 | analysis = subprocess.check_output(['symbol_why.py', '--dotconfig', '{}'.format( d.getVar('B') + '/.config' ), '--summary', '--extended', '--sanity', c], cwd=s, env=env ).decode('utf-8') | ||
449 | except subprocess.CalledProcessError as e: | ||
450 | bb.fatal( "config analysis failed: %s" % e.output.decode('utf-8')) | ||
451 | |||
452 | outfile = d.getVar( 'CONFIG_AUDIT_FILE' ) | ||
453 | |||
454 | if c: | ||
455 | outdir = os.path.dirname( outfile ) | ||
456 | outname = os.path.basename( outfile ) | ||
457 | outfile = outdir + '/'+ c + '-' + outname | ||
458 | |||
459 | if config and os.path.isfile(outfile): | ||
460 | os.remove(outfile) | ||
461 | |||
462 | with open(outfile, 'w+') as f: | ||
463 | f.write( analysis ) | ||
464 | |||
465 | bb.warn( "Configuration {} executed, see: {} for details".format(action,outfile )) | ||
466 | if c: | ||
467 | bb.warn( analysis ) | ||
468 | } | ||
408 | 469 | ||
409 | python do_kernel_configcheck() { | 470 | python do_kernel_configcheck() { |
410 | import re, string, sys, subprocess | 471 | import re, string, sys, subprocess |
@@ -414,57 +475,89 @@ python do_kernel_configcheck() { | |||
414 | # meta-series for processing | 475 | # meta-series for processing |
415 | kmeta = d.getVar("KMETA") or "meta" | 476 | kmeta = d.getVar("KMETA") or "meta" |
416 | if not os.path.exists(kmeta): | 477 | if not os.path.exists(kmeta): |
417 | kmeta = "." + kmeta | 478 | kmeta = subprocess.check_output(['kgit', '--meta']).decode('utf-8').rstrip() |
418 | 479 | ||
419 | s = d.getVar('S') | 480 | s = d.getVar('S') |
420 | 481 | ||
421 | env = os.environ.copy() | 482 | env = os.environ.copy() |
422 | env['PATH'] = "%s:%s%s" % (d.getVar('PATH'), s, "/scripts/util/") | 483 | env['PATH'] = "%s:%s%s" % (d.getVar('PATH'), s, "/scripts/util/") |
423 | env['LD'] = "${KERNEL_LD}" | 484 | env['LD'] = d.getVar('KERNEL_LD') |
485 | env['CC'] = d.getVar('KERNEL_CC') | ||
486 | env['ARCH'] = d.getVar('ARCH') | ||
487 | env['srctree'] = s | ||
424 | 488 | ||
425 | try: | 489 | try: |
426 | configs = subprocess.check_output(['scc', '--configs', '-o', s + '/.kernel-meta'], env=env).decode('utf-8') | 490 | configs = subprocess.check_output(['scc', '--configs', '-o', s + '/.kernel-meta'], env=env).decode('utf-8') |
427 | except subprocess.CalledProcessError as e: | 491 | except subprocess.CalledProcessError as e: |
428 | bb.fatal( "Cannot gather config fragments for audit: %s" % e.output.decode("utf-8") ) | 492 | bb.fatal( "Cannot gather config fragments for audit: %s" % e.output.decode("utf-8") ) |
429 | 493 | ||
430 | try: | ||
431 | subprocess.check_call(['kconf_check', '--report', '-o', | ||
432 | '%s/%s/cfg' % (s, kmeta), d.getVar('B') + '/.config', s, configs], cwd=s, env=env) | ||
433 | except subprocess.CalledProcessError: | ||
434 | # The configuration gathering can return different exit codes, but | ||
435 | # we interpret them based on the KCONF_AUDIT_LEVEL variable, so we catch | ||
436 | # everything here, and let the run continue. | ||
437 | pass | ||
438 | |||
439 | config_check_visibility = int(d.getVar("KCONF_AUDIT_LEVEL") or 0) | 494 | config_check_visibility = int(d.getVar("KCONF_AUDIT_LEVEL") or 0) |
440 | bsp_check_visibility = int(d.getVar("KCONF_BSP_AUDIT_LEVEL") or 0) | 495 | bsp_check_visibility = int(d.getVar("KCONF_BSP_AUDIT_LEVEL") or 0) |
441 | 496 | ||
442 | # if config check visibility is non-zero, report dropped configuration values | 497 | # if config check visibility is "1", that's the lowest level of audit. So |
443 | mismatch_file = d.expand("${S}/%s/cfg/mismatch.txt" % kmeta) | 498 | # we add the --classify option to the run, since classification will |
444 | if os.path.exists(mismatch_file): | 499 | # streamline the output to only report options that could be boot issues, |
445 | if config_check_visibility: | 500 | # or are otherwise required for proper operation. |
446 | with open (mismatch_file, "r") as myfile: | 501 | extra_params = "" |
502 | if config_check_visibility == 1: | ||
503 | extra_params = "--classify" | ||
504 | |||
505 | # category #1: mismatches | ||
506 | try: | ||
507 | analysis = subprocess.check_output(['symbol_why.py', '--dotconfig', '{}'.format( d.getVar('B') + '/.config' ), '--mismatches', extra_params], cwd=s, env=env ).decode('utf-8') | ||
508 | except subprocess.CalledProcessError as e: | ||
509 | bb.fatal( "config analysis failed: %s" % e.output.decode('utf-8')) | ||
510 | |||
511 | if analysis: | ||
512 | outfile = "{}/{}/cfg/mismatch.txt".format( s, kmeta ) | ||
513 | if os.path.isfile(outfile): | ||
514 | os.remove(outfile) | ||
515 | with open(outfile, 'w+') as f: | ||
516 | f.write( analysis ) | ||
517 | |||
518 | if config_check_visibility and os.stat(outfile).st_size > 0: | ||
519 | with open (outfile, "r") as myfile: | ||
447 | results = myfile.read() | 520 | results = myfile.read() |
448 | bb.warn( "[kernel config]: specified values did not make it into the kernel's final configuration:\n\n%s" % results) | 521 | bb.warn( "[kernel config]: specified values did not make it into the kernel's final configuration:\n\n%s" % results) |
449 | 522 | ||
450 | if bsp_check_visibility: | 523 | # category #2: invalid fragment elements |
451 | invalid_file = d.expand("${S}/%s/cfg/invalid.cfg" % kmeta) | 524 | extra_params = "" |
452 | if os.path.exists(invalid_file) and os.stat(invalid_file).st_size > 0: | 525 | if bsp_check_visibility > 1: |
453 | with open (invalid_file, "r") as myfile: | 526 | extra_params = "--strict" |
454 | results = myfile.read() | 527 | try: |
455 | bb.warn( "[kernel config]: This BSP sets config options that are not offered anywhere within this kernel:\n\n%s" % results) | 528 | analysis = subprocess.check_output(['symbol_why.py', '--dotconfig', '{}'.format( d.getVar('B') + '/.config' ), '--invalid', extra_params], cwd=s, env=env ).decode('utf-8') |
456 | errors_file = d.expand("${S}/%s/cfg/fragment_errors.txt" % kmeta) | 529 | except subprocess.CalledProcessError as e: |
457 | if os.path.exists(errors_file) and os.stat(errors_file).st_size > 0: | 530 | bb.fatal( "config analysis failed: %s" % e.output.decode('utf-8')) |
458 | with open (errors_file, "r") as myfile: | 531 | |
532 | if analysis: | ||
533 | outfile = "{}/{}/cfg/invalid.txt".format(s,kmeta) | ||
534 | if os.path.isfile(outfile): | ||
535 | os.remove(outfile) | ||
536 | with open(outfile, 'w+') as f: | ||
537 | f.write( analysis ) | ||
538 | |||
539 | if bsp_check_visibility and os.stat(outfile).st_size > 0: | ||
540 | with open (outfile, "r") as myfile: | ||
459 | results = myfile.read() | 541 | results = myfile.read() |
460 | bb.warn( "[kernel config]: This BSP contains fragments with errors:\n\n%s" % results) | 542 | bb.warn( "[kernel config]: This BSP contains fragments with warnings:\n\n%s" % results) |
461 | 543 | ||
462 | # if the audit level is greater than two, we report if a fragment has overriden | 544 | # category #3: redefined options (this is pretty verbose and is debug only) |
463 | # a value from a base fragment. This is really only used for new kernel introduction | 545 | try: |
464 | if bsp_check_visibility > 2: | 546 | analysis = subprocess.check_output(['symbol_why.py', '--dotconfig', '{}'.format( d.getVar('B') + '/.config' ), '--sanity'], cwd=s, env=env ).decode('utf-8') |
465 | redefinition_file = d.expand("${S}/%s/cfg/redefinition.txt" % kmeta) | 547 | except subprocess.CalledProcessError as e: |
466 | if os.path.exists(redefinition_file) and os.stat(redefinition_file).st_size > 0: | 548 | bb.fatal( "config analysis failed: %s" % e.output.decode('utf-8')) |
467 | with open (redefinition_file, "r") as myfile: | 549 | |
550 | if analysis: | ||
551 | outfile = "{}/{}/cfg/redefinition.txt".format(s,kmeta) | ||
552 | if os.path.isfile(outfile): | ||
553 | os.remove(outfile) | ||
554 | with open(outfile, 'w+') as f: | ||
555 | f.write( analysis ) | ||
556 | |||
557 | # if the audit level is greater than two, we report if a fragment has overriden | ||
558 | # a value from a base fragment. This is really only used for new kernel introduction | ||
559 | if bsp_check_visibility > 2 and os.stat(outfile).st_size > 0: | ||
560 | with open (outfile, "r") as myfile: | ||
468 | results = myfile.read() | 561 | results = myfile.read() |
469 | bb.warn( "[kernel config]: This BSP has configuration options defined in more than one config, with differing values:\n\n%s" % results) | 562 | bb.warn( "[kernel config]: This BSP has configuration options defined in more than one config, with differing values:\n\n%s" % results) |
470 | } | 563 | } |