Madhawa Priyashantha
Madhawa Priyashantha

Reputation: 9900

Output is incomplete when running jdb as a process from java application

I'm trying to debug a java class from java program. I created a simple class to test. This is my class hello.java and it lies in the folder C:\Users\madhawax\Desktop\beaufify\debugging

My problem is that I can't retrieve the part

VM Started: Set deferred breakpoint Hello.main
...

when I run jdb from java code, but when I manually run jdb from command line I can see it.

Why do I get only part of the real output? How can I fix this?

This is my Hello.java class:

public class Hello {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println("loop number "+i);
        }
    }
} 

I used 3 commands to run jdb

jdb
stop in Hello.main
run Hello

Console output when I debug manually using cmd .

C:\Users\madhawax\Desktop\beaufify\debugging>jdb
Initializing jdb ...
> stop in Hello.main
Deferring breakpoint Hello.main.
It will be set after the class is loaded.
> run Hello
run  Hello
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
>
VM Started: Set deferred breakpoint Hello.main

Breakpoint hit: "thread=main", Hello.main(), line=3 bci=0
3            for (int i = 0; i < 10; i++) {

main[1]

Output when I run jdb using java code .

run:
Initializing jdb ...
> Deferring breakpoint Hello.main.
It will be set after the class is loaded.
> run  Hello
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
> Input stream closed.
BUILD STOPPED (total time: 4 seconds)

I used this code to run jdb.

try {
    ProcessBuilder builder = new ProcessBuilder("C:\\Program Files\\Java\\jdk1.8.0_31\\bin\\jdb.exe");
    builder.directory(new File("C:\\Users\\madhawax\\Desktop\\beaufify\\debugging\\"));
    Process process = builder.start();
    OutputStream stdin = process.getOutputStream();
    InputStream stdout = process.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
    try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin))) {
        writer.write("stop in Hello.main\n");
        writer.flush();
        writer.write("run Hello");
        writer.flush();
    }
    String inputLine;
    Scanner scanner = new Scanner(stdout);
    while (scanner.hasNextLine()) {
        System.out.println(scanner.nextLine());
    }
 } catch (IOException ex) {
    ex.printStackTrace();
}

Upvotes: 1

Views: 943

Answers (1)

default locale
default locale

Reputation: 13456

I suggest you to run your code without try-with-resources:

Scanner scanner = new Scanner(stdout);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
writer.write("stop in Hello.main\n");
writer.flush();
writer.write("run Hello\n");
writer.flush();

while (scanner.hasNextLine()) {
    System.out.println(scanner.nextLine());
}

In your code try-with-resources will close BufferedWriter after execution:

try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin)))     {
    writer.write("stop in Hello.main\n");
    writer.flush();
    writer.write("run Hello");
    writer.flush();
} //writer is closed here

Thus, it will close underlying process output stream and this, apparently, leads to closure of jdb process.

You might want to change try-with-resource to try-catch-finally wrapping the whole method.

UPDATE: Also, it's a good idea to read output of one command before running next command. With your approach:

writer.write("stop in Hello.main\n");
writer.flush();
writer.write("run Hello\n");
writer.flush();
writer.write("list\n");
...

There's no pause between command calls. jdb might not be able to handle list command at the time (because it's launching VM). As an experiment you can introduce a time gap:

writer.flush();
Thread.sleep(1000);
writer.write("list\n");

The superior approach is to read output in between.

writer.flush();
readOutput(stdout);
writer.write("list\n");

You can use scanner to read the output. But, as @vandale pointed out in question comments, Scanner blocks on token and line breaks. You might want to use non-blocking reads to read available output. Something like this might work:

private void readOutput(InputStream outputStream) throws IOException{
    byte[] buffer = new byte[100000];
    int bytesRead;
    while (outputStream.available() > 0) {
        bytesRead = outputStream.read(buffer);
        if (bytesRead > 0) {
            System.out.print(new String(buffer, 0, bytesRead));
        }
    }
}

This code will also show output that doesn't end with a line break (input prompts, main[1], etc.)

Upvotes: 1

Related Questions