Skip to content

Commit

Permalink
Propagate exceptions in default binary file viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
jld01 committed Jun 26, 2023
1 parent 0dc9d34 commit 44dc9d7
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 36 deletions.
9 changes: 9 additions & 0 deletions NewAndNoteworthy/CDT-11.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ This is the New & Noteworthy page for CDT 11.3 which is part of Eclipse 2023-09

# Release Notes

# Build

## Default Binary File Viewer

Enhancements to the _Default Binary File Viewer_ accommodate archive files in the same manner as other binary files.
Users may now invoke GNU `objdump` by double-clicking on a GNU archive file.
In cases where the configured binary parser does not support the launching of GNU tools, the viewer now presents a hex dump of the binary file rather than raw text:

<p align="center"><img src="images/CDT-11.3-hex-dump.png" width="50%"></p>

# API Changes, current and planned

Expand Down
Binary file added NewAndNoteworthy/images/CDT-11.3-hex-dump.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@
package org.eclipse.cdt.internal.ui.editor;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.UncheckedIOException;
import java.text.MessageFormat;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

import org.apache.commons.io.HexDump;
import org.eclipse.cdt.core.IBinaryParser.IBinaryFile;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.IArchive;
import org.eclipse.cdt.core.model.IBinary;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.utils.IGnuToolFactory;
import org.eclipse.cdt.utils.Objdump;
import org.eclipse.core.resources.IFile;
Expand All @@ -59,41 +59,62 @@ public static class BinaryFileDocumentProvider extends FileDocumentProvider {

private static final String CONTENT_TRUNCATED_MESSAGE_FORMAT = "\n--- {0} ---\n"; //$NON-NLS-1$

private InputStream getObjdumpInputStream(Objdump objdump) throws IOException {
// limit editor to X MB, if more - users should use objdump in command
// this is UI blocking call, on 56M binary it takes more than 15 min
// and generates at least 2.5G of assembly
int limitBytes = 6 * 1024 * 1024; // this can run reasonably within seconds
byte[] output = objdump.getOutput(limitBytes);
if (output.length >= limitBytes) {
// append a message for user
String message = "\n" + MessageFormat.format(CONTENT_TRUNCATED_MESSAGE_FORMAT, //$NON-NLS-1$
CEditorMessages.DefaultBinaryFileEditor_TruncateMessage) + objdump.toString();
System.arraycopy(message.getBytes(), 0, output, limitBytes - message.length(), message.length());
}
return new ByteArrayInputStream(output);
}

private InputStream getHexDumpInputStream(IPath filePath) throws IOException {
private InputStream getInputStream(Consumer<OutputStream> writer) throws IOException {
final AtomicReference<IOException> writerException = new AtomicReference<>();
final PipedInputStream pipedInputStream = new PipedInputStream();
final FilterInputStream filterInputStream = new FilterInputStream(pipedInputStream) {
@Override
public void close() throws IOException {
try {
final IOException exception = writerException.get();
if (exception != null) {
// propagate pipe writer exception to pipe reader
throw new IOException(exception.getMessage(), exception);
}
} finally {
super.close();
}
}
};
final OutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream);
new Thread(() -> {
try {
writeHexDump(filePath, pipedOutputStream);
try (pipedOutputStream) {
writer.accept(pipedOutputStream);
} catch (UncheckedIOException e) {
writerException.set(e.getCause());
} catch (IOException e) {
CUIPlugin.log(e);
} finally {
try {
pipedOutputStream.close();
} catch (IOException e) {
CUIPlugin.log(e);
}
writerException.set(e);
}
}).start();
return pipedInputStream;
return filterInputStream;
}

private void writeHexDump(IPath filePath, OutputStream outputStream) throws IOException {
private void writeObjdump(Objdump objdump, OutputStream outputStream) {
try (InputStream objdumpStream = objdump.getInputStream()) {
int offset = 0;
while (true) {
// read objdump content via 4 KiB buffer
final byte[] buffer = objdumpStream.readNBytes(4096);
if (0 == buffer.length) { // end of file stream
break;
}
// limit to 16 MiB objdump content
if (offset >= 0x1000000) {
// append a message for user
String message = "\n" + MessageFormat.format(CONTENT_TRUNCATED_MESSAGE_FORMAT, //$NON-NLS-1$
CEditorMessages.DefaultBinaryFileEditor_TruncateMessage) + objdump.toString();
outputStream.write(message.getBytes());
break;
}
outputStream.write(buffer);
offset += buffer.length;
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private void writeHexDump(IPath filePath, OutputStream outputStream) {
final int BYTES_PER_LINE = 16; // hard-coded in HexDump class - do not modify
try (InputStream fileStream = new BufferedInputStream(new FileInputStream(filePath.toFile()))) {
int offset = 0;
Expand All @@ -103,7 +124,7 @@ private void writeHexDump(IPath filePath, OutputStream outputStream) throws IOEx
if (0 == buffer.length) { // end of file stream
break;
}
// limit content to 16MiB data
// limit to 16 MiB binary file content
if (offset >= 0x1000000) {
// append a message for user
String message = MessageFormat.format(CONTENT_TRUNCATED_MESSAGE_FORMAT,
Expand All @@ -114,6 +135,8 @@ private void writeHexDump(IPath filePath, OutputStream outputStream) throws IOEx
HexDump.dump(buffer, offset, outputStream, 0);
offset += buffer.length;
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

Expand All @@ -125,11 +148,11 @@ private InputStream getBinaryFileContent(IBinaryFile binaryFile) throws CoreExce
Objdump objdump = factory.getObjdump(filePath);
if (objdump != null) {
// use output from objdump tool
return getObjdumpInputStream(objdump);
return getInputStream(stream -> writeObjdump(objdump, stream));
}
}
// fall back to a hex dump if objdump tool not available
return getHexDumpInputStream(binaryFile.getPath());
return getInputStream(stream -> writeHexDump(filePath, stream));
} catch (IOException e) {
String message = (e.getMessage() != null ? e.getMessage() : ""); //$NON-NLS-1$
throw new CoreException(Status.error(message, e));
Expand All @@ -142,13 +165,12 @@ protected boolean setDocumentContent(IDocument document, IEditorInput editorInpu
if (editorInput instanceof IFileEditorInput fileEditorInput) {
IFile file = fileEditorInput.getFile();
ICElement cElement = CoreModel.getDefault().create(file);
if (cElement instanceof IArchive || cElement instanceof IBinary) {
if (cElement != null) {
IBinaryFile binaryFile = cElement.getAdapter(IBinaryFile.class);
if (binaryFile != null) {
setDocumentContent(document, getBinaryFileContent(binaryFile), encoding);
return true;
}
return false;
}
}
return super.setDocumentContent(document, editorInput, encoding);
Expand Down

0 comments on commit 44dc9d7

Please sign in to comment.