Skip to content

Commit 4ef8cce

Browse files
committed
logging: Add better logging on IPC server-side failures
Motivation: when trying to add a new unit test ran into a confusing error "(remote):0: failed: remote exception: Called null capability" caused by forgetting to make a FooInterface.initThreadMap call during initialization and these logging prints would have made it easier to see where the error was coming from and debug.
1 parent 47d79db commit 4ef8cce

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

include/mp/proxy-types.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,13 @@ kj::Promise<void> serverInvoke(Server& server, CallContext& call_context, Fn fn)
719719
.then([&server, req](CallContext call_context) {
720720
server.m_context.loop->log() << "IPC server send response #" << req << " " << TypeName<Results>()
721721
<< " " << LogEscape(call_context.getResults().toString(), server.m_context.loop->m_log_opts.max_chars);
722+
}, [&server, req](::kj::Exception&& e) {
723+
// Call failed for some reason. Cap'n Proto will try to send
724+
// this this error to the client as well, but it is good to log
725+
// the failure early here and include the request number.
726+
server.m_context.loop->log() << "IPC server error request #" << req << " " << TypeName<Results>()
727+
<< " " << kj::str("kj::Exception: ", e).cStr();
728+
return kj::mv(e);
722729
});
723730
} catch (const std::exception& e) {
724731
server.m_context.loop->log() << "IPC server unhandled exception: " << e.what();

include/mp/type-context.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,25 @@ auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn&
158158
<< "IPC server error request #" << req << ", missing thread to execute request";
159159
throw std::runtime_error("invalid thread handle");
160160
}
161+
}, [&server, req](::kj::Exception&& e) {
162+
// Detect when getLocalServer call fails. The serverInvoke function
163+
// will print the full exception object after this, but it is worth
164+
// printing an additional message here because a failure here is
165+
// probably caused by failing to pass a ThreadMap object from the
166+
// server to the client before this call. Specifically what happens
167+
// if there was not a prior IPC call returning a ThreadMap, is that
168+
// the client's Connection::m_thread_map member will be null when
169+
// the client is making this IPC, and when it uses
170+
// m_thread_map.makeThreadRequest() as part of this IPC, the
171+
// makeThread request will appear to succeed due to Cap'n Proto's
172+
// request pipelining, but actually return a null Thread::Client
173+
// capability, and when that capability is passed to getLocalServer
174+
// it will fail with an error here: "(remote):0: failed: remote
175+
// exception: Called null capability" that's pretty opaque, so good
176+
// to print a more specific message.
177+
server.m_context.loop->log()
178+
<< "IPC server error request #" << req << " CapabilityServerSet<Thread>::getLocalServer call failed, did you forget to provide a ThreadMap to the client prior to this IPC call?";
179+
return kj::mv(e);
161180
})
162181
// Wait for the invocation to finish before returning to the caller.
163182
.then([invoke_wait = kj::mv(future.promise)]() mutable { return kj::mv(invoke_wait); });

0 commit comments

Comments
 (0)