Bug 401618 - Incorrect exceptions thrown from org.eclipse.cdt.utils.spawner.SpawnerInputStream.read0(Native Method)
Summary: Incorrect exceptions thrown from org.eclipse.cdt.utils.spawner.SpawnerInputSt...
Status: NEW
Alias: None
Product: CDT
Classification: Tools
Component: cdt-debug (show other bugs)
Version: Next   Edit
Hardware: PC Windows All
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: cdt-debug-inbox@eclipse.org CLA
QA Contact: Jonah Graham CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-02-24 07:27 EST by Dave Korn CLA
Modified: 2020-09-04 15:23 EDT (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Dave Korn CLA 2013-02-24 07:27:41 EST
Debugging a CDT plugin that I've been working on, I've been seeing exceptions thrown from the native code in spawner.dll that don't have an adequate error message.

The code in question is catching and dumping the exception in a pretty straightforward fashion, running the following in a thread:

        try {
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line;
            while ((line = br.readLine()) != null) {
            	if (consumer != null) {
            		consumer.recvString(this, line);
            	}
                if (isLogging && isError)
                    logError(line);
                else if (isLogging)
                    logInfo(line);
            }
        } catch (Exception ex) {
            logError("Error capturing output", ex);
        }

and it generates output like this:

!ENTRY org.eclipse.cdt.debug.gdbsim.core 4 0 2013-02-24 09:13:20.578
!MESSAGE Error capturing output
!STACK 0
java.io.IOException: W
	at org.eclipse.cdt.utils.spawner.SpawnerInputStream.read0(Native Method)
	at org.eclipse.cdt.utils.spawner.SpawnerInputStream.read(SpawnerInputStream.java:66)
	at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
	at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
	at java.io.InputStreamReader.read(InputStreamReader.java:167)
	at java.io.BufferedReader.fill(BufferedReader.java:136)
	at java.io.BufferedReader.readLine(BufferedReader.java:299)
	at java.io.BufferedReader.readLine(BufferedReader.java:362)
	at org.eclipse.cdt.debug.gdbsim.core.helpers.StreamGobbler.run(StreamGobbler.java:64)

Note that the exception only appears to have a single character error message.

I believe I've tracked down the underlying cause of the problem: unicode-vs-ansi confusion in the native code.  (See http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/tree/core/org.eclipse.cdt.core.win32/library/iostream.c)

The iostream.c code repeatedly uses this idiom to generate java exceptions:

		char * lpMsgBuf;
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			GetLastError(),
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(wchar_t *) &lpMsgBuf,
			0,
			NULL 
		);

		ThrowByName(env, "java/io/IOException", lpMsgBuf);
		// Free the buffer.
		LocalFree( lpMsgBuf );
 
Note in particular the cast to (wchar_t *).  The fact that FormatMessage is expecting a pointer to wide char here indicates that the Unicode version of the function, FormatMessageW is being called - presumably because the UNICODE define is in effect when the DLL is being built.  That means that lpMsgBuf is being filled with wide chars, but the ThrowByName function:

void ThrowByName(JNIEnv *env, const char *name, const char *msg);

... is expecting a buffer of plain old ANSI single-byte ASCII chars. As the high byte of all the wide chars is zero, the buffer full of wide chars passed to ThrowByName just looks like a single char followed by a NUL terminator, hence the single character message in the java exception when it is caught.

The simple fix would be to explicitly call FormatMessageA, removing the (wchar_t *) cast in the process.  Another alternative would be to ensure the entire DLL is built without -DUNICODE enabled, but I don't know what more widespread consequences that might have.

From an internationalization point of view, it would be best to keep the Unicode error message and create a ThrowByNameW function that accepts a wide char error message buffer, but that would be a good deal more work, as the JNI does not provide a simple interface method to construct and throw an exception with a unicode string (ThrowByName uses JNIEnv->ThrowNew); at a first glance, it would probably be necessary to manually construct the java exception object, set the message, and call JNIEnv->Throw.

I observed this problem with the spawner.dll included in org.eclipse.cdt.core.win32_5.2.0.201006141710 but the code in iostream.c is still unchanged in git HEAD according to the git.eclipse.org link above, so the bug will still be present in Unicode-enabled builds of the DLL.  I'm not going to have time to contribute a patch for this, but I hope the diagnosis is useful, and adopting the FormatMessageA fix should be a very small amount of work for anyone who is already set up to develop the DLL.