Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CpsFlowExecution: parseScript(): log "Method Too Large" situations more readably #817

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

jimklimov
Copy link

A small almost-cosmetic change which makes life easier for my colleagues when they develop (and debug failures of) Jenkins scripted pipelines.

@jimklimov jimklimov requested a review from a team as a code owner November 23, 2023 08:35
@bitwiseman
Copy link
Contributor

Out of curiosity, what output does this give you and how is it helpful?

jimklimov and others added 5 commits December 11, 2023 21:55
…to avoid catching by full name

Signed-off-by: Jim Klimov <jimklimov+jenkinsci@gmail.com>
…argeExceptionRealistic() tests

Signed-off-by: Jim Klimov <jimklimov+jenkinsci@gmail.com>
…sException as a carrier of MethodTooLargeException; re-throw with just a compact message to appear in the build log

Signed-off-by: Jim Klimov <jimklimov+jenkinsci@gmail.com>
@jimklimov
Copy link
Author

jimklimov commented Mar 5, 2024

Tests helped me realize that the catch block was a bit naive; the groovy parser returns a special exception class with a collection of errors, one of which (okay, the only one) is the Method Too Large one.

PR updated with handling of both eventualities, and if the only exception in view is the MTL - re-throw with a short message avoiding a wall of text in pipeline log. From self-test console (showing both server log id=... and pipeline log test0), it now complains much more readably, like this:

   6.903 [test0 #1] Started
   7.635 [id=99]	SEVERE	o.j.p.w.cps.CpsFlowExecution#parseScript: FAILED to parse WorkflowScript (the pipeline script) due to MethodTooLargeException: Method too large: WorkflowScript.___cps___1 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;
   7.680 [test0 #1] java.lang.RuntimeException: FAILED to parse WorkflowScript (the pipeline script) due to MethodTooLargeException: Method too large: WorkflowScript.___cps___1 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;
   7.680 [test0 #1] 	at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:697)
   7.680 [test0 #1] 	at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:586)
   7.681 [test0 #1] 	at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
   7.681 [test0 #1] 	at hudson.model.ResourceController.execute(ResourceController.java:101)
   7.681 [test0 #1] 	at hudson.model.Executor.run(Executor.java:442)
   7.700 [test0 #1] Finished: FAILURE
   7.718 [id=27]	INFO	hudson.lifecycle.Lifecycle#onStatusUpdate: Stopping Jenkins
   7.766 [id=27]	INFO	hudson.lifecycle.Lifecycle#onStatusUpdate: Jenkins stopped

Still got a bit of stack trace from generic handling, but it is reasonably short - the ultimate pipeline devs won't have to scroll up a couple of screenfuls just to learn that their script was too long or complex.

Now that the message is visible to end users, I am open to suggestions how to make it more actionable, and/or if the RuntimeException is the right re-throw wrapper, and of course which color should the bike shed be (or this can all be part of another PR) :D

e.g. to explain about script length or its coding complexity and what to do about it (nesting, amount of stages, separation of methods into JSL classes, etc.); maybe point to a knowledge-base URL for a better maintainable article on the subject (don't want to replace one wall of text in the logs with another, right?)

@jglick jglick requested a review from a team March 5, 2024 12:22
…unt to satisfy different CI platforms

Signed-off-by: Jim Klimov <jimklimov+jenkinsci@gmail.com>
…t importing the class

Signed-off-by: Jim Klimov <jimklimov+jenkinsci@gmail.com>
…uggestions for pipeline devs

Signed-off-by: Jim Klimov <jimklimov+jenkinsci@gmail.com>
… expected matching line

Signed-off-by: Jim Klimov <jimklimov+jenkinsci@gmail.com>
@jimklimov
Copy link
Author

jimklimov commented Mar 5, 2024

Out of curiosity, what output does this give you and how is it helpful?

Until today, it was helpful to myself and a few colleagues who may/can/do follow traces of the Jenkins server logs when developing pipelines (testing in private Jenkins instances and those wrapped by IDEs notwithstanding).

After the review comments some months ago (thanks, and sorry for not noticing) and a coding effort today, this should be better visible and more "actionable" to a more general population - now shown as a smaller wall of text right in the pipeline build logs:

   5.380 [test0 #1] Started
   5.975 [test0 #1] java.lang.RuntimeException: FAILED to parse WorkflowScript
       (the pipeline script) due to MethodTooLargeException; please refactor to
       simplify code structure and/or move logic to a Jenkins Shared Library:
       Method too large: WorkflowScript.___cps___1 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;
   5.975 [test0 #1] 	at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:703)
   5.975 [test0 #1] 	at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:584)
   5.975 [test0 #1] 	at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
   5.975 [test0 #1] 	at hudson.model.ResourceController.execute(ResourceController.java:101)
   5.975 [test0 #1] 	at hudson.model.Executor.run(Executor.java:442)
   6.011 [test0 #1] Finished: FAILURE

...which is IMHO a bit more comprehensible than the original (especially the first time you see it):

...
 > git checkout -f c391f78bf7ac0d4afffab4f312a3d81cc41577d1 # timeout=10
Commit message: "..."
 > git rev-list --no-walk c391f78bf7ac0d4afffab4f312a3d81cc41577d1 # timeout=10
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during class generation: Method too large: WorkflowScript.___cps___20692 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;

groovyjarjarasm.asm.MethodTooLargeException: Method too large: WorkflowScript.___cps___20692 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;
        at groovyjarjarasm.asm.MethodWriter.computeMethodInfoSize(MethodWriter.java:2087)
        at groovyjarjarasm.asm.ClassWriter.toByteArray(ClassWriter.java:447)
        at org.codehaus.groovy.control.CompilationUnit$17.call(CompilationUnit.java:850)
        at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1087)
        at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
        at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
        at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
        at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
        at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
        at groovy.lang.GroovyShell.parseClass(GroovyShell.java:677)
        at groovy.lang.GroovyShell.parse(GroovyShell.java:689)
        at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:142)
        at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
        at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:554)
        at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:506)
        at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
        at hudson.model.ResourceController.execute(ResourceController.java:101)
        at hudson.model.Executor.run(Executor.java:442)

1 error

        at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:309)
        at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1107)
        at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
        at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
        at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
        at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
        at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
        at groovy.lang.GroovyShell.parseClass(GroovyShell.java:677)
        at groovy.lang.GroovyShell.parse(GroovyShell.java:689)
        at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:142)
        at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
        at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:554)
        at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:506)
        at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
        at hudson.model.ResourceController.execute(ResourceController.java:101)
        at hudson.model.Executor.run(Executor.java:442)
Finished: FAILURE

...and requires less scrolling to get to the crux of the issue =D

jimklimov and others added 2 commits March 5, 2024 15:52
…ethods (class complexity) from maxTryCatch (nesting depth)

Signed-off-by: Jim Klimov <jimklimov+jenkinsci@gmail.com>
Copy link
Member

@jglick jglick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic here is pretty convoluted and seems excessive when we do not really need to make things particularly pretty, we just need to offer some clue beyond what there is today. Would suffice to do something along the lines of

if (Functions.printThrowable(x).containsString("MethodTooLargeException")) {
    throw new IllegalArgumentException("…your hint here…", x);
} else {
    throw x;
}

Comment on lines -640 to +642
} catch (RuntimeException | Error x) {
} catch (Exception x) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loses handling of Error, especially LinkageError.

Comment on lines +690 to +692
// Make a note in server log
LOGGER.log(Level.SEVERE, msg);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not; this is up to the owner of the project to fix, not the server admin.

Suggested change
// Make a note in server log
LOGGER.log(Level.SEVERE, msg);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants