Add proper file-based error reporting

This commit is contained in:
Jack Andersen 2016-08-02 12:12:49 -10:00
parent e17d5c0b83
commit 577af720d3
3 changed files with 156 additions and 109 deletions

View File

@ -3,9 +3,12 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <signal.h>
#include <system_error>
#include <string>
#include <algorithm>
#include <chrono>
#include <thread>
#include <hecl/hecl.hpp>
#include <hecl/Database.hpp>
@ -103,7 +106,7 @@ size_t BlenderConnection::_readLine(char* buf, size_t bufSz)
*buf = '\0';
if (readBytes >= 4)
if (!memcmp(buf, "EXCEPTION", std::min(readBytes, size_t(9))))
BlenderLog.report(logvisor::Fatal, "Blender exception");
_blenderDied();
return readBytes;
}
++readBytes;
@ -114,7 +117,7 @@ size_t BlenderConnection::_readLine(char* buf, size_t bufSz)
*buf = '\0';
if (readBytes >= 4)
if (!memcmp(buf, "EXCEPTION", std::min(readBytes, size_t(9))))
BlenderLog.report(logvisor::Fatal, "Blender exception");
_blenderDied();
return readBytes;
}
}
@ -131,7 +134,7 @@ size_t BlenderConnection::_writeLine(const char* buf)
goto err;
return (size_t)ret;
err:
BlenderLog.report(logvisor::Fatal, strerror(errno));
_blenderDied();
return 0;
}
@ -142,10 +145,10 @@ size_t BlenderConnection::_readBuf(void* buf, size_t len)
goto err;
if (len >= 4)
if (!memcmp((char*)buf, "EXCEPTION", std::min(len, size_t(9))))
BlenderLog.report(logvisor::Fatal, "Blender exception");
_blenderDied();
return ret;
err:
BlenderLog.report(logvisor::Fatal, strerror(errno));
_blenderDied();
return 0;
}
@ -156,7 +159,7 @@ size_t BlenderConnection::_writeBuf(const void* buf, size_t len)
goto err;
return ret;
err:
BlenderLog.report(logvisor::Fatal, strerror(errno));
_blenderDied();
return 0;
}
@ -166,9 +169,30 @@ void BlenderConnection::_closePipe()
close(m_writepipe[1]);
}
void BlenderConnection::_blenderDied()
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
FILE* errFp = hecl::Fopen(m_errPath.c_str(), _S("r"));
if (errFp)
{
fseek(errFp, 0, SEEK_END);
int64_t len = hecl::FTell(errFp);
if (len)
{
fseek(errFp, 0, SEEK_SET);
std::unique_ptr<char[]> buf(new char[len+1]);
memset(buf.get(), 0, len+1);
fread(buf.get(), 1, len, errFp);
BlenderLog.report(logvisor::Fatal, "\n%s", buf.get());
}
}
BlenderLog.report(logvisor::Fatal, "Blender Exception");
}
BlenderConnection::BlenderConnection(int verbosityLevel)
{
BlenderLog.report(logvisor::Info, "Establishing BlenderConnection...");
signal(SIGPIPE, SIG_IGN);
/* Put hecl_blendershell.py in temp dir */
#ifdef _WIN32
@ -320,6 +344,10 @@ BlenderConnection::BlenderConnection(int verbosityLevel)
m_blenderProc = pid;
#endif
/* Stash error path an unlink existing file */
m_errPath = hecl::SystemString(TMPDIR) + hecl::Format(_S("/hecl_%016llX.derp"), (unsigned long long)m_blenderProc);
hecl::Unlink(m_errPath.c_str());
/* Handle first response */
char lineBuf[256];
_readLine(lineBuf, sizeof(lineBuf));
@ -398,7 +426,7 @@ BlenderConnection::PyOutStream::StreamBuf::overflow(int_type ch)
{
if (m_deleteOnError)
m_parent.m_parent->deleteBlend();
BlenderLog.report(logvisor::Fatal, "error sending '%s' to blender", m_lineBuf.c_str());
m_parent.m_parent->_blenderDied();
}
m_lineBuf.clear();
return ch;

View File

@ -61,11 +61,13 @@ private:
bool m_loadedRigged = false;
ProjectPath m_loadedBlend;
std::string m_startupBlend;
hecl::SystemString m_errPath;
size_t _readLine(char* buf, size_t bufSz);
size_t _writeLine(const char* buf);
size_t _readBuf(void* buf, size_t len);
size_t _writeBuf(const void* buf, size_t len);
void _closePipe();
void _blenderDied();
public:
BlenderConnection(int verbosityLevel=1);
~BlenderConnection();

View File

@ -10,10 +10,19 @@ args = sys.argv[sys.argv.index('--')+1:]
readfd = int(args[0])
writefd = int(args[1])
verbosity_level = int(args[2])
err_path = ""
if sys.platform == "win32":
import msvcrt
readfd = msvcrt.open_osfhandle(readfd, os.O_RDONLY | os.O_BINARY)
writefd = msvcrt.open_osfhandle(writefd, os.O_WRONLY | os.O_BINARY)
err_path = "/Temp"
else:
err_path = "/tmp"
if 'TMPDIR' in os.environ:
err_path = os.environ['TMPDIR']
err_path += "/hecl_%016X.derp" % os.getpid()
def readpipeline():
retval = bytearray()
@ -230,125 +239,133 @@ def dataout_loop():
writepipebuf(struct.pack('f', c))
# Command loop
while True:
cmdargs = read_cmdargs()
print(cmdargs)
# Main exception handling
try:
# Command loop
while True:
cmdargs = read_cmdargs()
print(cmdargs)
if cmdargs[0] == 'QUIT':
quitblender()
if cmdargs[0] == 'QUIT':
quitblender()
elif cmdargs[0] == 'OPEN':
if 'FINISHED' in bpy.ops.wm.open_mainfile(filepath=cmdargs[1]):
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode = 'OBJECT')
writepipeline(b'FINISHED')
else:
writepipeline(b'CANCELLED')
elif cmdargs[0] == 'CREATE':
if len(cmdargs) >= 4:
bpy.ops.wm.open_mainfile(filepath=cmdargs[3])
else:
bpy.ops.wm.read_homefile()
bpy.context.user_preferences.filepaths.save_version = 0
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdargs[1]):
bpy.ops.file.hecl_patching_load()
bpy.context.scene.hecl_type = cmdargs[2]
writepipeline(b'FINISHED')
else:
writepipeline(b'CANCELLED')
elif cmdargs[0] == 'GETTYPE':
writepipeline(bpy.context.scene.hecl_type.encode())
elif cmdargs[0] == 'GETMESHRIGGED':
meshName = bpy.context.scene.hecl_mesh_obj
if meshName not in bpy.data.objects:
writepipeline(b'FALSE')
else:
if len(bpy.data.objects[meshName].vertex_groups):
writepipeline(b'TRUE')
elif cmdargs[0] == 'OPEN':
if 'FINISHED' in bpy.ops.wm.open_mainfile(filepath=cmdargs[1]):
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode = 'OBJECT')
writepipeline(b'FINISHED')
else:
writepipeline(b'CANCELLED')
elif cmdargs[0] == 'CREATE':
if len(cmdargs) >= 4:
bpy.ops.wm.open_mainfile(filepath=cmdargs[3])
else:
bpy.ops.wm.read_homefile()
bpy.context.user_preferences.filepaths.save_version = 0
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdargs[1]):
bpy.ops.file.hecl_patching_load()
bpy.context.scene.hecl_type = cmdargs[2]
writepipeline(b'FINISHED')
else:
writepipeline(b'CANCELLED')
elif cmdargs[0] == 'GETTYPE':
writepipeline(bpy.context.scene.hecl_type.encode())
elif cmdargs[0] == 'GETMESHRIGGED':
meshName = bpy.context.scene.hecl_mesh_obj
if meshName not in bpy.data.objects:
writepipeline(b'FALSE')
else:
if len(bpy.data.objects[meshName].vertex_groups):
writepipeline(b'TRUE')
else:
writepipeline(b'FALSE')
elif cmdargs[0] == 'SAVE':
bpy.context.user_preferences.filepaths.save_version = 0
if 'FINISHED' in bpy.ops.wm.save_mainfile(check_existing=False, compress=True):
writepipeline(b'FINISHED')
else:
writepipeline(b'CANCELLED')
elif cmdargs[0] == 'SAVE':
bpy.context.user_preferences.filepaths.save_version = 0
if 'FINISHED' in bpy.ops.wm.save_mainfile(check_existing=False, compress=True):
writepipeline(b'FINISHED')
else:
writepipeline(b'CANCELLED')
elif cmdargs[0] == 'PYBEGIN':
writepipeline(b'READY')
globals = {'hecl':hecl}
compbuf = str()
bracket_count = 0
while True:
try:
line = readpipeline()
elif cmdargs[0] == 'PYBEGIN':
writepipeline(b'READY')
globals = {'hecl':hecl}
compbuf = str()
bracket_count = 0
while True:
try:
line = readpipeline()
# ANIM check
if line == b'PYANIM':
# Ensure remaining block gets executed
# ANIM check
if line == b'PYANIM':
# Ensure remaining block gets executed
if len(compbuf):
exec_compbuf(compbuf, globals)
compbuf = str()
animin_loop(globals)
continue
# End check
elif line == b'PYEND':
# Ensure remaining block gets executed
if len(compbuf):
exec_compbuf(compbuf, globals)
compbuf = str()
writepipeline(b'DONE')
break
# Syntax filter
linestr = line.decode().rstrip()
if not len(linestr) or linestr.lstrip()[0] == '#':
writepipeline(b'OK')
continue
leading_spaces = len(linestr) - len(linestr.lstrip())
# Block lines always get appended right away
if linestr.endswith(':') or leading_spaces or bracket_count:
if len(compbuf):
compbuf += '\n'
compbuf += linestr
bracket_count += count_brackets(linestr)
writepipeline(b'OK')
continue
# Complete non-block statement in compbuf
if len(compbuf):
exec_compbuf(compbuf, globals)
compbuf = str()
animin_loop(globals)
continue
# End check
elif line == b'PYEND':
# Ensure remaining block gets executed
if len(compbuf):
exec_compbuf(compbuf, globals)
compbuf = str()
writepipeline(b'DONE')
break
# Syntax filter
linestr = line.decode().rstrip()
if not len(linestr) or linestr.lstrip()[0] == '#':
writepipeline(b'OK')
continue
leading_spaces = len(linestr) - len(linestr.lstrip())
# Block lines always get appended right away
if linestr.endswith(':') or leading_spaces or bracket_count:
if len(compbuf):
compbuf += '\n'
compbuf += linestr
# Establish new compbuf
compbuf = linestr
bracket_count += count_brackets(linestr)
writepipeline(b'OK')
continue
# Complete non-block statement in compbuf
if len(compbuf):
exec_compbuf(compbuf, globals)
except Exception as e:
writepipeline(b'EXCEPTION')
raise
break
writepipeline(b'OK')
# Establish new compbuf
compbuf = linestr
bracket_count += count_brackets(linestr)
elif cmdargs[0] == 'PYEND':
writepipeline(b'ERROR')
elif cmdargs[0] == 'DATABEGIN':
try:
dataout_loop()
except Exception as e:
writepipeline(b'EXCEPTION')
raise
break
writepipeline(b'OK')
elif cmdargs[0] == 'PYEND':
writepipeline(b'ERROR')
elif cmdargs[0] == 'DATAEND':
writepipeline(b'ERROR')
elif cmdargs[0] == 'DATABEGIN':
try:
dataout_loop()
except Exception as e:
writepipeline(b'EXCEPTION')
raise
elif cmdargs[0] == 'DATAEND':
writepipeline(b'ERROR')
else:
hecl.command(cmdargs, writepipeline, writepipebuf)
else:
hecl.command(cmdargs, writepipeline, writepipebuf)
except Exception:
import traceback
fout = open(err_path, 'w')
traceback.print_exc(file=fout)
fout.close()
raise