Skip to content

Commit cc08c23

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 cc08c23

File tree

2 files changed

+41
-0
lines changed

2 files changed

+41
-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 error to the client as well, but it is good to log the
725+
// 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: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,40 @@ 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+
// If you see the error "(remote):0: failed: remote exception:
163+
// Called null capability" here, it probably means your Init class
164+
// is missing a declaration like:
165+
//
166+
// construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
167+
//
168+
// which passes a ThreadMap reference from the client to the server,
169+
// allowing the server to create threads to run IPC calls on the
170+
// client, and also returns a ThreadMap reference from the server to
171+
// the client, allowing the client to create threads on the server.
172+
// (Typically the latter ThreadMap is used more often because there
173+
// are more client-to-server calls.)
174+
//
175+
// If the other side of the connection did not previously get a
176+
// ThreadMap reference from this side of the connection, when the
177+
// other side calls `m_thread_map.makeThreadRequest()` in
178+
// `BuildField` above, `m_thread_map` will be null, but that call
179+
// will not fail immediately due to Cap'n Proto's request pipelining
180+
// and delayed execution. Instead that call will return an invalid
181+
// Thread reference, and when that reference is passed to this side
182+
// of the connection as `thread_client` above, the
183+
// `getLocalServer(thread_client)` call there will be the first
184+
// thing to overtly fail, leading to an error here.
185+
//
186+
// Potentially there are also other things that could cause errors
187+
// here, but this is the mostly likely cause.
188+
//
189+
// The log statement here is not strictly necessary since the same
190+
// exception will also be logged in serverInvoke, but this logging
191+
// may provide extra context that could be helpful for debugging.
192+
server.m_context.loop->log()
193+
<< "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?";
194+
return kj::mv(e);
161195
})
162196
// Wait for the invocation to finish before returning to the caller.
163197
.then([invoke_wait = kj::mv(future.promise)]() mutable { return kj::mv(invoke_wait); });

0 commit comments

Comments
 (0)