Debugging

Last modified by Simon Urli on 2023/10/10 11:46

There are several possibilities for debugging XWiki:

Debug mode

[Since 7.1]

It's possible to enable debug log by adding debug=true (as in http://127.0.0.1:8080/xwiki/bin/Main/WebHome?debug=true) to the URL. This provide various details on what exactly happens during the request.

Elapsed time tree

A detailed tree of how long has been spent in each step. You can add you own step to the tree by using progress API.

elapsedtimetree.png

Debugging XWiki from your IDE

Debugging with Eclipse

Once you've gotten the debugger working, you'll wonder how you ever survived without it.

Complete tutorial based on M2E and WTP

Debug XWiki with Eclipse

Debug a XWiki released version

  • Follow the steps from Building In Eclipse including the optional "Import the WAR as a web project".
  • Select from the Eclipse menu Run > Debug...
  • Create a new configuration for your server
  • Hit the Debug button
  • Set breakpoints, step through code, have fun!

WTP will deploy XWiki into {workspace_location}/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/webapps/xwiki. If you suspect a change has not been deployed correctly (e.g. a change to a config file), check there to confirm.

If you are working on an XWiki plugin in a separate project in your workspace, you can ensure it is automatically deployed to XWiki's WEB-INF/lib directory when debugging. Open the project properties of the XWiki project resulting from the WAR import above, go to the J2EE Module Dependencies section, and add your plugin project.

Using Firebug when debugging a selenium test

For that you will need to install Firebug as a global extension

Remote Debugging

To perform remote debugging, start your wiki in debug mode. To do this you can:

  • use the start_xwiki_debug.sh executable if you're using the Standalone packaging
  • modify the way you start your container and ensure that the following JVM parameters are passed:
    -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005

    For example, for Jetty you could use the following start script:

    #!/bin/sh

    JETTY_HOME=.
    JETTY_PORT=8080
    JAVA_OPTS="-Xmx300m -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"

    java $JAVA_OPTS -Dfile.encoding=iso-8859-1 -Djetty.port=$JETTY_PORT -Djetty.home=$JETTY_HOME -jar $JETTY_HOME/start.jar

    If you are using the Tomcat service on Windows, you should modify the JVM args with the Tomcat Service Configuration Panel. Select the Java tab and add each of the options on a separate line. For example:

    -Dcatalina.home=C:\Program Files\Apache Software Foundation\Tomcat 5.5
    -Dcatalina.base=C:\Program Files\Apache Software Foundation\Tomcat 5.5
    -Djava.endorsed.dirs=C:\Program Files\Apache Software Foundation\Tomcat 5.5\common\endorsed
    -Djava.io.tmpdir=C:\Program Files\Apache Software Foundation\Tomcat 5.5\temp
    -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
    -Djava.util.logging.config.file=C:\Program Files\Apache Software Foundation\Tomcat 5.5\conf\logging.properties
    -Xdebug
    -Xnoagent
    -Djava.compiler=NONE  
    -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n

Then, in your favorite IDE, open the XWiki project and run a remote debugging session, attached to the socket on port 5005. For example, if you're using IntelliJ IDEA, go to Run|Edit Configurations... and create a new Remote configuration (default parameters should be fine). Then execute it and IDEA will connect to the executing JVM.

You can then place breakpoints in your source code.

Debugging options for start_xwiki_debug.sh:

  • Use the --suspend parameter to tell the JVM to wait until someone connect to the debugging port before progressing.
  • Use the JETTY_DEBUG_PORT environment variable to control the JVM debug port to use (default is 5005).
  • XWiki 12.8+ Use the -dp <number> (or --debugPort <number>) parameter to control the JVM debug port to use (default is 5005).

Logging

Turning on logging inside XWiki

See the Logging page in the Admin guide.

Logging shutdown operations

XWiki 6.3M1+ To log debug information of what happens when there are shutdown operations, simply set the log level to DEBUG for the org.xwiki.shutdown logger:

<logger name="org.xwiki.shutdown" level="info"/>

Logging calls at the database level

You have several options:

  • Configure Hibernate to log SQL calls. To do this edit XWiki's log configuration file (see the Logging page) and change the log level from warn to trace for the following:
      <!-- Log HQL query parser activity -->
      <logger name="org.hibernate.hql.ast.AST" level="warn"/>
      <!-- Log just the SQL -->
      <logger name="org.hibernate.SQL" level="warn"/>
      <!-- Log JDBC bind parameters -->
      <logger name="org.hibernate.type" level="warn"/>
      <!-- Log schema export/update -->
      <logger name="org.hibernate.tool.hbm2ddl" level="warn"/>
      <!-- Log HQL parse trees -->
      <logger name="org.hibernate.hql" level="warn"/>
      <!-- Log cache activity -->
      <logger name="org.hibernate.cache" level="warn"/>
      <!-- Log transaction activity -->
      <logger name="org.hibernate.transaction" level="warn"/>
      <!-- Log JDBC resource acquisition -->
      <logger name="org.hibernate.jdbc" level="warn"/>
      <!-- Enable the following line if you want to track down connection leakages when using
           DriverManagerConnectionProvider -->
      <logger name="org.hibernate.connection.DriverManagerConnectionProvider" level="warn"/>
      <!-- Log prepared statement cache activity -->
      <logger name="org.hibernate.ps.PreparedStatementCache" level="warn"/>
  • Use a wrapping JDBC driver such as log4jdbc or p6spy.
  • Turn on logging in your database. The configuration is database-dependent*

    For HSQLDB. Two solutions:* Solution 1: Edit WEB-INF/hibernate.cfg.xml and modify the URL connection property to add hsqldb.sqllog=3, as in:

    <property name="connection.url">jdbc:hsqldb:file:${environment.permanentDirectory}/database/xwiki_db;shutdown=true;hsqldb.sqllog=3</property>
    • Solution 2: Using Byteman (See the section below for more information). For example the following rule (tested on HSQLDB 2.3.1) will log SQL statements with parameters and schema when there's more than 1 parameter:
      RULE Log SQL Statement
      CLASS org.hsqldb.Session
      METHOD executeCompiledStatement(org.hsqldb.Statement, Object[], int)
      AT ENTRY
      IF $2.length > 0
      DO traceln("SQL Statement = [" + $1.getSQL() + "], parameter 1 = [" + $2[0] + "], schema = [" + $1.getSchemaName().name + "]")
      ENDRULE

      Will give for example:

      SQL Statement = [select xwikidocum0_.XWD_ID as XWD1_0_0_, xwikidocum0_.XWD_FULLNAME as XWD2_0_0_, xwikidocum0_.XWD_NAME as XWD3_0_0_, xwikidocum0_.XWD_TITLE as XWD4_0_0_, xwikidocum0_.XWD_LANGUAGE as XWD5_0_0_, xwikidocum0_.XWD_DEFAULT_LANGUAGE as XWD6_0_0_, xwikidocum0_.XWD_TRANSLATION as XWD7_0_0_, xwikidocum0_.XWD_DATE as XWD8_0_0_, xwikidocum0_.XWD_CONTENT_UPDATE_DATE as XWD9_0_0_, xwikidocum0_.XWD_CREATION_DATE as XWD10_0_0_, xwikidocum0_.XWD_AUTHOR as XWD11_0_0_, xwikidocum0_.XWD_CONTENT_AUTHOR as XWD12_0_0_, xwikidocum0_.XWD_CREATOR as XWD13_0_0_, xwikidocum0_.XWD_WEB as XWD14_0_0_, xwikidocum0_.XWD_CONTENT as XWD15_0_0_, xwikidocum0_.XWD_VERSION as XWD16_0_0_, xwikidocum0_.XWD_CUSTOM_CLASS as XWD17_0_0_, xwikidocum0_.XWD_PARENT as XWD18_0_0_, xwikidocum0_.XWD_CLASS_XML as XWD19_0_0_, xwikidocum0_.XWD_ELEMENTS as XWD20_0_0_, xwikidocum0_.XWD_DEFAULT_TEMPLATE as XWD21_0_0_, xwikidocum0_.XWD_VALIDATION_SCRIPT as XWD22_0_0_, xwikidocum0_.XWD_COMMENT as XWD23_0_0_, xwikidocum0_.XWD_MINOREDIT as XWD24_0_0_, xwikidocum0_.XWD_SYNTAX_ID as XWD25_0_0_, xwikidocum0_.XWD_HIDDEN as XWD26_0_0_ from xwikidoc xwikidocum0_ where xwikidocum0_.XWD_ID=?], parameter 1 = [-4526159677276379501], schema = [TEST91]

Logging HTTP calls

XWiki uses HTTP Client in most places and logging what it does is easy. You'll need to add the following to XWiki's logback.xml file:

<logger name="org.apache.http" level="debug"/>
<logger name="org.apache.http.headers" level="debug"/>
<logger name="org.apache.http.wire" level="debug"/>

Using Byteman

Byteman is a great framework that allows to modify bytecode in a running JVM. It can easily be used to add logging (for ex) to find out what's happening in a running XWiki instance. Here's a quick tutorial:

  • Download Byteman and unzip it in a directory. Set the $BYTEMAN_HOME environment property to point to the directory where you've unzipped it. For example:
    export BYTEMAN_HOME=/Users/vmassol/dev/byteman/byteman-download-2.1.0
  • Start an XWiki instance somewhere on the same machine
  • Go to $BYTEMAN_HOME/bin and connect Byteman to XWiki. For example:
    sh bminstall.sh -b -Dorg.jboss.byteman.transform.all jetty/start.jar
  • Now create a Byteman rule by creating a file, for example my.btm with the following:
    RULE XWiki Docs loaded
    CLASS XWikiHibernateStore
    METHOD loadXWikiDoc
    AT ENTRY
    IF TRUE
    DO traceln("Load document = " + $1.getDocumentReference())
    ENDRULE
  • Load this rule with: sh bmsubmit.sh -l my.btm
  • Call a URL in your wiki and check that the console prints the document references that get loaded! For example:
    Load document = xwiki:Main.WebPreferences
    Load document = xwiki:ColorThemes.DefaultColorTheme
    Load document = xwiki:XWiki.SearchSuggestConfig
    Load document = xwiki:XWiki.SearchSuggestConfig
    Load document = xwiki:Dashboard.WebHome
    Load document = xwiki:XWiki.GadgetClass
    Load document = xwiki:Dashboard.WebHome
    Load document = xwiki:Dashboard.WebPreferences
    Load document = xwiki:Dashboard.colibri
    Load document = xwiki:Main.Welcome
    ...
  • Modify your rule or add new rules to the same file and apply the changes with sh bmsubmit.sh -l my.btm
  • Remove your rules with sh bmsubmit.sh -u

For more read A Byteman tutorial or the Reference documentation.

Examples

Example 1

In this example we'll print calls to CommonsConfigurationSource.containsKey(...) with the parameters printed and the time it takes to execute. In addition we'll also print calls to XWiki.getDocument(String, XWikiContext) which is a deprecated method which, for the purpose of this example, we think generate the calls to containsKey.

RULE Create timer for containsKey
CLASS CommonsConfigurationSource
METHOD containsKey
AT ENTRY
IF TRUE
DO resetTimer("containsKey")
ENDRULE

RULE Calls to containsKey
CLASS CommonsConfigurationSource
METHOD containsKey
AT EXIT
IF TRUE
DO traceln("containsKey for [" + $1 + "] = [" + $! + "], time = [" + getElapsedTimeFromTimer("containsKey") + "]")
ENDRULE

RULE Calls to deprecated getDocument
CLASS com.xpn.xwiki.XWiki
METHOD getDocument(String, XWikiContext)
AT ENTRY
IF TRUE
DO traceStack("getDocument() called for [" + $1 + "", 3)
ENDRULE

Generates:

...
getDocument() called for [XWiki.Admin] com.xpn.xwiki.XWiki.getDocument(XWiki.java:-1)
com.xpn.xwiki.XWiki.getUserName(XWiki.java:5072)
com.xpn.xwiki.XWiki.getUserName(XWiki.java:5062)
  . . .
containsKey for [model.reference.default.document] = [false], time = [0]
containsKey for [model.reference.default.document] = [false], time = [0]
containsKey for [model.reference.default.space] = [false], time = [0]
containsKey for [model.reference.default.space] = [false], time = [0]
containsKey for [model.reference.default.wiki] = [false], time = [0]
containsKey for [model.reference.default.wiki] = [false], time = [1]
getDocument() called for [XWiki.DefaultSkin] com.xpn.xwiki.XWiki.getDocument(XWiki.java:-1)
com.xpn.xwiki.XWiki.getSkinFile(XWiki.java:1955)
com.xpn.xwiki.XWiki.getSkinFile(XWiki.java:1918)
  . . .
...

Example 2

Log calls to XWikiDocument#setLock() and XWikiDocument#removeLock().

RULE XWiki Set Lock
CLASS XWikiDocument
METHOD setLock
AT ENTRY
IF TRUE
DO traceln("set lock for user [" + $1 + "], doc = [" + $this + "]")
ENDRULE

RULE XWiki Remove Lock
CLASS XWikiDocument
METHOD removeLock
AT ENTRY
IF TRUE
DO traceln("remove lock for doc = [" + $this + "]")
ENDRULE

RULE XWiki Get Lock
CLASS XWikiDocument
METHOD getLock
AT ENTRY
IF TRUE
DO traceln("get lock for doc = [" + $this + "]")
ENDRULE

When you click edit and then cancel on a doc you get for example:

// Edit
get lock for doc = [Sandbox.WebHome]
set lock for user [XWiki.Admin], doc = [Sandbox.WebHome]
get lock for doc = [Sandbox.WebHome]
get lock for doc = [Sandbox.WebHome]
set lock for user [XWiki.Admin], doc = [Sandbox.WebHome]

// Cancel
get lock for doc = [Sandbox.WebHome]
remove lock for doc = [Sandbox.WebHome]

When you click edit and then save on a doc you get for example:

// Edit
get lock for doc = [Sandbox.WebHome]
set lock for user [XWiki.Admin], doc = [Sandbox.WebHome]
get lock for doc = [Sandbox.WebHome]
get lock for doc = [Sandbox.WebHome]
set lock for user [XWiki.Admin], doc = [Sandbox.WebHome]

// Save
get lock for doc = [Sandbox.WebHome]
remove lock for doc = [Sandbox.WebHome]
get lock for doc = [Sandbox.WebHome]

Troubleshooting

NoClassDefFoundError: com/sun/tools/attach/AttachNotSupportedException

If you get the following exception, it can mean that you're on Mac and that your tools.jar is not added to the classpath. Apparently bminstall.sh thinks that if you're on Mac then you don't need tools.jar which is apparently wrong nowadays.

Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/tools/attach/AttachNotSupportedException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: com.sun.tools.attach.AttachNotSupportedException
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more

In this case, edit bminstall.sh and add the following line below the OS=`uname` line in order to override it:

OS=whatever

Debugging JavaScript

The solution is to use the developer tools provided by the Browser for inspecting JavaScript and putting breakpoints in it. By default the XWiki build will minify and aggregate JavaScript files for performance reasons.

XWiki 12.7-rc-1+

Open the developer tools provided by the browser and put the breakpoints using the Debugger / Sources tab. Look for the non-minified (source) JavaScript code (Ctrl+P). You should be able to find it most of the time because the minified version (which is used by default) indicates its source map which in turn indicates the source code. So the browser ends up loading all 3 when the developer tools are open (shouldn't be the case for end users), which means you can run the minified version while viewing the progress step by step over the source (non-minified) version. If the source map is missing or the source URL is wrong then you can still debug step by step using the source code by adding ?minify=false to the URL of the current page. This will force the browser to load the non-minified version (and only that).

Note that JavaScript minification is not only about removing whitespace and renaming local variables. Depending on the minifier used it may also mean code optimizations, syntax suggar replacement, polyfill injection, etc. So the differences between the minified version and the source code could be significant. As a consequence:

  • debugging step by step may behave strangely when there's no 1 to 1 mapping between the minified code and its source
  • the non-minified (source) version might not work on all browsers; that's why we recommend using ?minify=false only with the latest versions of Firefox and Chrome.

XWiki 7.1+ You can also set debug.minify to false in xwiki.properties file instead of passing ?minify=false in the URL.

XWiki <12.7-rc-1

You had to do the following:

  • Build XWiki with the debug profile (mvn clean install -Pdebug)
  • Once you're on a page make sure to pass ?minify=false in the URL. If you don't do this then the aggregated JS files are going to be referenced and since they don't exist, the JS code will break. 

Debugging Vue.js

The first step is to load a non-minified version of vue.js. To do so, edit webapps/xwiki/resources/uicomponents/widgets/liveData.min.js and replace 'vue': $services.webjars.url('vue', 'vue.min') by 'vue': $services.webjars.url('vue', 'vue').
Then, install Vue Devtools in Firefox or Chrome.
You should now have access to the Vue tab in you browser debugger, with the ability to inspect the Vue applications deployed in the page.

Debugging the WYSIWYG editor

Example to debug JS code in macroSelector.js:

  • Modify the top level CKEditor pom.xml to remove minification (the important part is to add --leave-js-unminified
    as on the example below):
    ...
           <plugin>
             <groupId>org.codehaus.mojo</groupId>
             <artifactId>exec-maven-plugin</artifactId>
             <executions>
               <!-- Build the CKEditor. -->
               <execution>
                 <id>build-ckeditor</id>
                 <!-- Use a phase after compile because the Clirr Maven Plugin executes all the phases up to compile twice. -->
                 <phase>process-classes</phase>
                 <goals>
                   <goal>exec</goal>
                 </goals>
                 <configuration>
                   <executable>${ckeditor.builder.path}/build.sh</executable>
                   <arguments>
                     <!-- Exclude from release all plugins/skins that are not specified in build-config. -->
                     <argument>--skip-omitted-in-build-config</argument>
                     <argument>-s --leave-js-unminified</argument>
                   </arguments>
                 </configuration>
               </execution>
             </executions>
           </plugin>
    ...
  • Rebuild the application-ckeditor-webjar module (you'll also need to rebuildapplication-ckeditor-plugins if you made changes to it).
  • Copy the generated application-ckeditor-webjar-<version>.jar to an XWiki instance, in WEB-INF/lib and restart XWiki
    • You could also remove the CKEditor extensions in the permanent's directory (in extensions/repository) to be safe.
  • In the wiki, edit CKEditor.EditSheet in object mode and look for #set ($macroWizardBundlePath = "${ckeditorBasePath}plugins/xwiki-macro/macroWizard.bundle.min") and remove the .min part. Note that in this case you have to know that the macro wizard is loaded separately in macroWizard.bundle.min.js. Otherwise the JS is in ckeditor.js.
  • Use your browser's dev tools to put breakpoints in the JS.

Analyze Out Of Memory issues

You can enable automatic memory dump when using -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/where/to/save/the/memory/dumps  which will generate a memory dump as soon as Java detect an Out Of memory error. See https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/clopts001.html#CHDFDIJI for more details.

Since 7.2RC1 the option is enabled by default in Jetty based distributions and the memory dump will ends up in data/ folder.

Get Connected