C backend: exit application and print stack when getting out of memory error

This commit is contained in:
Alexey Andreev 2019-04-26 14:04:07 +03:00
parent 87656a3e9a
commit 20866637e7
6 changed files with 112 additions and 10 deletions

View File

@ -34,7 +34,8 @@ public class StringPoolGenerator {
if (codes) { if (codes) {
generateNumericStringLiteral(s); generateNumericStringLiteral(s);
} else { } else {
generateSimpleStringLiteral(s); writer.print("u");
generateSimpleStringLiteral(writer, s);
} }
writer.print(")"); writer.print(")");
@ -55,9 +56,9 @@ public class StringPoolGenerator {
return false; return false;
} }
private void generateSimpleStringLiteral(String string) { public static void generateSimpleStringLiteral(CodeWriter writer, String string) {
if (string.isEmpty()) { if (string.isEmpty()) {
writer.print("u\"\""); writer.print("\"\"");
return; return;
} }
@ -67,7 +68,7 @@ public class StringPoolGenerator {
writer.println(); writer.println();
} }
int last = Math.min(i + chunkSize, string.length()); int last = Math.min(i + chunkSize, string.length());
writer.print("u\""); writer.print("\"");
for (int j = i; j < last; ++j) { for (int j = i; j < last; ++j) {
char c = string.charAt(j); char c = string.charAt(j);

View File

@ -15,7 +15,10 @@
*/ */
package org.teavm.backend.c.intrinsic; package org.teavm.backend.c.intrinsic;
import org.teavm.ast.ConstantExpr;
import org.teavm.ast.Expr;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.c.generate.StringPoolGenerator;
import org.teavm.interop.Strings; import org.teavm.interop.Strings;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -28,11 +31,18 @@ public class StringsIntrinsic implements Intrinsic {
@Override @Override
public void apply(IntrinsicContext context, InvocationExpr invocation) { public void apply(IntrinsicContext context, InvocationExpr invocation) {
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
case "toC": case "toC": {
context.writer().print("teavm_stringToC("); Expr arg = invocation.getArguments().get(0);
context.emit(invocation.getArguments().get(0)); String literal = extractStringConstant(arg);
context.writer().print(")"); if (literal != null) {
StringPoolGenerator.generateSimpleStringLiteral(context.writer(), literal);
} else {
context.writer().print("teavm_stringToC(");
context.emit(arg);
context.writer().print(")");
}
break; break;
}
case "fromC": case "fromC":
context.writer().print("teavm_cToString("); context.writer().print("teavm_cToString(");
context.emit(invocation.getArguments().get(0)); context.emit(invocation.getArguments().get(0));
@ -40,4 +50,13 @@ public class StringsIntrinsic implements Intrinsic {
break; break;
} }
} }
private String extractStringConstant(Expr expr) {
if (!(expr instanceof ConstantExpr)) {
return null;
}
Object value = ((ConstantExpr) expr).getValue();
return value instanceof String ? (String) value : null;
}
} }

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.runtime;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged;
@Unmanaged
@StaticInit
public final class Console {
private Console() {
}
@Import(name = "teavm_printString")
public static native void printString(Address ptr);
@Import(name = "teavm_printInt")
public static native void printInt(int n);
}

View File

@ -18,6 +18,7 @@ package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Export; import org.teavm.interop.Export;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Strings;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
@ -28,6 +29,32 @@ public final class ExceptionHandling {
public static native CallSite findCallSiteById(int id); public static native CallSite findCallSiteById(int id);
public static void printStack() {
Address stackFrame = ShadowStack.getNextStackFrame(ShadowStack.getStackTop());
while (stackFrame != null) {
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId);
CallSiteLocation location = callSite.location;
Console.printString(Strings.toC(" at "));
if (location.className == null || location.methodName == null) {
Console.printString(Strings.toC("(Unknown method)"));
} else {
Console.printString(Strings.toC(location.className));
Console.printString(Strings.toC("."));
Console.printString(Strings.toC(location.methodName));
}
Console.printString(Strings.toC("("));
if (location.fileName != null && location.lineNumber >= 0) {
Console.printString(Strings.toC(location.fileName));
Console.printString(Strings.toC(":"));
Console.printInt(location.lineNumber);
}
Console.printString(Strings.toC(")\n"));
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
}
}
private static Throwable thrownException; private static Throwable thrownException;
@Export(name = "sys_catchException") @Export(name = "sys_catchException")

View File

@ -16,6 +16,7 @@
package org.teavm.runtime; package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
@ -46,6 +47,9 @@ public final class GC {
private static native int regionSize(); private static native int regionSize();
@Import(name = "teavm_outOfMemory")
private static native void outOfMemory();
public static int getFreeMemory() { public static int getFreeMemory() {
return freeMemory; return freeMemory;
} }
@ -88,7 +92,10 @@ public final class GC {
return; return;
} }
collectGarbage(size); collectGarbage(size);
getAvailableChunkIfPossible(size); if (!getAvailableChunkIfPossible(size)) {
ExceptionHandling.printStack();
outOfMemory();
}
} }
private static boolean getAvailableChunkIfPossible(int size) { private static boolean getAvailableChunkIfPossible(int size) {

View File

@ -1,5 +1,6 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -351,4 +352,17 @@ static void teavm_interrupt() {
raise(SIGRTMIN); raise(SIGRTMIN);
} }
#endif #endif
static void teavm_outOfMemory() {
fprintf(stderr, "Application crashed due to lack of free memory\n");
exit(1);
}
static void teavm_printString(char* s) {
fprintf(stderr, "%s", s);
}
static void teavm_printInt(int32_t i) {
fprintf(stderr, "%" PRId32, i);
}