Deobfuscated stack trace message and line-specific stack traces

533 views
Skip to first unread message

brad

unread,
Nov 5, 2018, 1:59:15 PM11/5/18
to GWT Users
Hi,

Two issues that often frustrate me with GWT deobfuscated stack traces:
a) The stack trace is deobfuscated, but the error message is not, so you get something like "com.google.gwt.core.client.JavaScriptException: (TypeError) : Cannot read property 'a' of undefined".
b) The stack trace refers to methods headers, rather than specific lines.

In many cases, neither of these is a big deal, but if the message is obscure and the method in question is long it can make debugging difficult. Is there anything that can be done to work around this (compiler properties, etc.). This is particularly for debugging a deployed application, not locally using super dev mode.

Thanks,
Brad

Paul Robinson

unread,
Nov 5, 2018, 2:11:24 PM11/5/18
to google-we...@googlegroups.com
I always deploy a version compiled with emulated stack traces alongside the regular version so that if a problem can be replicated, I can get a proper stack trace.

But I still share your pain. Trying to work out which line of obfuscated JavaScript could possibly have given a null pointer exception can be quite tedious.

I'd be (happily) surprised if it's possible to improve stack traces or error messages without slowing things down.

Paul

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-tool...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

Jens

unread,
Nov 5, 2018, 4:16:23 PM11/5/18
to GWT Users
Depending on your browser versions you support, you could try enabling source maps for other user agents as well. Currently it is only active for safari permutation. If you deploy the generated source maps, the StackTraceDeobfuscator could use them on the server to make better stack traces.

You would need to add some additional rebind rules to your project in the same manner as in https://github.com/gwtproject/gwt/blob/master/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml

At the end it all depends on wether or not the browser reports column numbers in addition to line numbers then throwing an error. Because in GWT generated JS every method has its own line and is a one-liner (white spaces, new lines within method removed) itself, browsers which only report line numbers basically only report the JS method that caused the error. Only with a column number it is possible to also figure out the expression within the method. Of course GWT could technically produce a JS file which has every single statement/expression on its own line, but that would add a significant amount of new line characters and thus would make your app larger (even though gzip compression might mitigate that issue). If GWT would do that, then column numbers wouldn't be required.

Using the above source maps based solution only works in browsers which support column numbers because source maps encoding is based on column numbers. See http://www.mattzeunert.com/2016/02/14/how-do-source-maps-work.html if interested.

-- J.

Pavlo Iatsiuk

unread,
Nov 13, 2018, 10:09:25 AM11/13/18
to GWT Users
Hi, 

I do not know about version 2.8, but for 2.6 we did next:
 - put value "PRETTY" in the pom.xml for gwt plugin
 - put next loggin settings in the application gwt config file

<inherits name="com.google.gwt.logging.Logging"/>
<set-property name="gwt.logging.logLevel" value="FINEST"/>
<set-property name="gwt.logging.consoleHandler" value="ENABLED"/>
<set-property name="gwt.logging.simpleRemoteHandler" value="DISABLED" />
<set-property name="gwt.logging.popupHandler" value="DISABLED" />


<set-property name="compiler.stackMode" value="emulated" />
<set-configuration-property name="compiler.emulatedStack.recordLineNumbers" value="true"/>
<set-configuration-property name="compiler.emulatedStack.recordFileNames" value="true"/>


as result we had a proper class names and line numbers in the stack-trace massages

понеділок, 5 листопада 2018 р. 19:59:15 UTC+1 користувач brad написав:

Craig Mitchell

unread,
May 16, 2019, 1:13:25 AM5/16/19
to GWT Users
Using GWT 2.8.2.  I started seeing errors being picked up by the GWT.setUncaughtExceptionHandler.  So, I implemented the StackTraceDeobfuscator by adding the following to my .gwt.xml:

<set-property name="compiler.stackMode" value="emulated" />
<set-configuration-property name="compiler.emulatedStack.recordLineNumbers" value="true" />
<set-configuration-property name="compiler.emulatedStack.recordFileNames" value="true"/>

Passing the exception back to the server RemoteServiceServlet, and doing the following:

String path = getServletConfig().getServletContext().getRealPath("/WEB-INF/deploy/<module>/symbolMaps/");
StackTraceDeobfuscator deobfuscator = StackTraceDeobfuscator.fromFileSystem(path);
String strongName = getThreadLocalRequest().getHeader(RpcRequestBuilder.STRONG_NAME_HEADER);

// Deobfuscate the stack trace
deobfuscator
.deobfuscateStackTrace(exception, strongName);

// Convert the stack trace to a string
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception
.printStackTrace(pw);
stackTrace
= sw.toString();

Now, when I look at what is saved in stackTrace, it is things like this:

java.lang.ClassCastException
 at sun
.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun
.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
 at sun
.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at java
.lang.reflect.Constructor.newInstance(Constructor.java:423)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.instantiate(ServerSerializationStreamReader.java:1110)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:682)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.readObject(ServerSerializationStreamReader.java:592)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader$ValueReader$8.readValue(ServerSerializationStreamReader.java:149)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserializeValue(ServerSerializationStreamReader.java:434)
 at com
.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:312)
 at com
.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:296)
 at com
.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:373)
 at com
.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
 at javax
.servlet.http.HttpServlet.service(HttpServlet.java:707)
 at javax
.servlet.http.HttpServlet.service(HttpServlet.java:790)
 at org
.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:848)
 at org
.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1772)
 at team
.drift.server.ServletFilter.doFilter(ServletFilter.java:37)
 at org
.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
 at com
.google.apphosting.utils.servlet.JdbcMySqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
 at org
.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
 at org
.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:582)
 at org
.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
 at org
.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524)
 at org
.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
 at org
.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
 at org
.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
 at com
.google.apphosting.runtime.jetty9.ParseBlobUploadHandler.handle(ParseBlobUploadHandler.java:119)
 at org
.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1182)
 at com
.google.apphosting.runtime.jetty9.AppEngineWebAppContext.doHandle(AppEngineWebAppContext.java:183)
 at org
.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:512)
 at org
.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
 at org
.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
 at org
.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
 at com
.google.apphosting.runtime.jetty9.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:293)
 at org
.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
 at org
.eclipse.jetty.server.Server.handle(Server.java:539)
 at org
.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:333)
 at com
.google.apphosting.runtime.jetty9.RpcConnection.handle(RpcConnection.java:213)
 at com
.google.apphosting.runtime.jetty9.RpcConnector.serviceRequest(RpcConnector.java:81)
 at com
.google.apphosting.runtime.jetty9.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:123)
 at com
.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchServletRequest(JavaRuntime.java:692)
 at com
.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchRequest(JavaRuntime.java:655)
 at com
.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:625)
 at com
.google.apphosting.runtime.JavaRuntime$NullSandboxRequestRunnable.run(JavaRuntime.java:817)
 at com
.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:269)
 at java
.lang.Thread.run(Thread.java:748)

I also save the user agent, which was:
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36

I was hoping for a more meaningful stack trace.

I also tried an exception that I raised, and I get the same stack trace:

team.drift.common.model.DriftTeamException: Sorry, your browser does not support WebGL.
 at sun
.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun
.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
 at sun
.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at java
.lang.reflect.Constructor.newInstance(Constructor.java:423)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.instantiate(ServerSerializationStreamReader.java:1110)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:682)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.readObject(ServerSerializationStreamReader.java:592)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader$ValueReader$8.readValue(ServerSerializationStreamReader.java:149)
 at com
.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserializeValue(ServerSerializationStreamReader.java:434)
 at com
.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:312)
 
...

Am I doing something wrong?

Thanks.

Craig Mitchell

unread,
May 16, 2019, 5:56:33 PM5/16/19
to GWT Users
I also tried compiling with Pretty and Detailed, both with the same result as Obfuscated.

When I debug the exception passed back to the server, the exception doesn't have any detail about the JS line that is occurred.  So, I don't understand how the StackTraceDeobfuscator would ever work?

Annotation 2019-05-17 075404.png


Any help much appreciated.

Thanks again!  :-)

Alexander Leshkin

unread,
May 17, 2019, 2:40:11 AM5/17/19
to GWT Users
Do you wrapping exception with SerializableThrowable (com.google.gwt.core.shared.SerializableThrowable.fromThrowable(Throwable)) before sending to server?

Craig Mitchell

unread,
May 17, 2019, 6:48:47 PM5/17/19
to GWT Users
Thank you Alexander!  That's what I was missing.

Called:
exception = SerializableThrowable.fromThrowable(exception);

before sending to the server, and now the stack traces show perfectly.  :-)

Craig Mitchell

unread,
Feb 23, 2024, 11:27:12 PMFeb 23
to GWT Users
The symbol maps worked great with the Eclipse GWT plugin compiler.

Now switched to use the Maven compiler (with the https://github.com/tbroyer/gwt-maven-archetypes architecture), I don't see the symbol maps anywhere when doing a mvn clean package.

Do I need to do something extra to get them?

Thanks.

George Paret

unread,
Feb 24, 2024, 8:45:57 AMFeb 24
to GWT Users
I see the symbolmaps generated in ` target/gwt/deploy/<modulename>/symbolMaps `

I believe it is generated by default.

Craig Mitchell

unread,
Feb 24, 2024, 6:30:14 PMFeb 24
to GWT Users
Hi George,

What I did was:
  1. Run https://github.com/tbroyer/gwt-maven-archetypes to generate the sample app.
  2. Modify module.gwt.xml to have: <set-property name="compiler.stackMode" value="native" />
  3. Run mvn clean package.
Then when I look inside mywebapp-server\target\mywebapp-server-HEAD-SNAPSHOT.war I see the mywebapp folder with all the compiled javascript, but no symbolMaps.

I also tried with:
<set-property name="compiler.stackMode" value="emulated" />
<set-configuration-property name="compiler.emulatedStack.recordLineNumbers" value="true" />
<set-configuration-property name="compiler.emulatedStack.recordFileNames" value="true"/>

Still no symbol maps.

Any ideas on what I'm missing would be great.  Thanks.

Craig Mitchell

unread,
Feb 24, 2024, 6:34:44 PMFeb 24
to GWT Users
Sorry, I do see them in  mywebapp-client\target\gwt\deploy\mywebapp\symbolMaps

How do I get them from there, into the war?

Thomas Broyer

unread,
Feb 25, 2024, 4:03:27 PMFeb 25
to GWT Users
On Sunday, February 25, 2024 at 12:34:44 AM UTC+1 ma...@craig-mitchell.com wrote:
Sorry, I do see them in  mywebapp-client\target\gwt\deploy\mywebapp\symbolMaps

How do I get them from there, into the war?

Configure <deploy> to somewhere inside the <webappDirectory> (preferably inside WEB-INF so they won't be served)

Craig Mitchell

unread,
Feb 26, 2024, 5:56:45 AMFeb 26
to GWT Users
Thanks Thomas, now working great!

For anyone coming from the Eclipse GWT plugin, you can do this to keep it consistant with how it used to work:

In your client pom.  Add the <deploy> like this:
<build>
  <plugins>
    <plugin>
      <groupId>net.ltgt.gwt.maven</groupId>
      <artifactId>gwt-maven-plugin</artifactId>
      <configuration>
        <moduleName>com.mycompany.mywebapp.App</moduleName>
        <moduleShortName>mywebapp</moduleShortName>
        <deploy>${project.build.directory}/mywebapp-client-HEAD-SNAPSHOT/WEB-INF/deploy</deploy>
      </configuration>
    </plugin>
  </plugins>
</build>

And then instead of reading the symbol maps directly like this:
  String path = config.getServletContext().getRealPath("/WEB-INF/deploy/<module>/symbolMaps/");
  StackTraceDeobfuscator deobfuscator = StackTraceDeobfuscator.fromFileSystem(path);

You now read them from inside the war like this:
  StackTraceDeobfuscator deobfuscator = StackTraceDeobfuscator.fromResource("/WEB-INF/deploy/<module>/symbolMaps/");

Cheers.

Thomas Broyer

unread,
Feb 26, 2024, 12:06:12 PMFeb 26
to GWT Users
On Monday, February 26, 2024 at 11:56:45 AM UTC+1 ma...@craig-mitchell.com wrote:
Thanks Thomas, now working great!

For anyone coming from the Eclipse GWT plugin, you can do this to keep it consistant with how it used to work:

In your client pom.  Add the <deploy> like this:
<build>
  <plugins>
    <plugin>
      <groupId>net.ltgt.gwt.maven</groupId>
      <artifactId>gwt-maven-plugin</artifactId>
      <configuration>
        <moduleName>com.mycompany.mywebapp.App</moduleName>
        <moduleShortName>mywebapp</moduleShortName>
        <deploy>${project.build.directory}/mywebapp-client-HEAD-SNAPSHOT/WEB-INF/deploy</deploy>

nit: use ${project.build.finalName} rather than hard-coding the "mywebapp-client-HEAD-SNAPSHOT"

Craig Mitchell

unread,
Feb 28, 2024, 5:44:17 AMFeb 28
to GWT Users
Thanks again Thomas.  👍

<build>
  <plugins>
    <plugin>
      <groupId>net.ltgt.gwt.maven</groupId>
      <artifactId>gwt-maven-plugin</artifactId>
      <configuration>
        <moduleName>com.mycompany.mywebapp.App</moduleName>
        <moduleShortName>mywebapp</moduleShortName>
        <deploy>${project.build.directory}/${project.build.finalName}/WEB-INF/deploy</deploy>
      </configuration>
    </plugin>
  </plugins>
</build>

Reply all
Reply to author
Forward
0 new messages