Skip to content

Commit 3a3e892

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 290702c commit 3a3e892

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
@@ -753,6 +753,13 @@ kj::Promise<void> serverInvoke(Server& server, CallContext& call_context, Fn fn)
753753
MP_LOG(*server.m_context.loop, Log::Debug) << "IPC server send response #" << req << " " << TypeName<Results>();
754754
MP_LOG(*server.m_context.loop, Log::Trace) << "response data: "
755755
<< LogEscape(call_context.getResults().toString(), server.m_context.loop->m_log_opts.max_chars);
756+
}, [&server, req](::kj::Exception&& e) {
757+
// Call failed for some reason. Cap'n Proto will try to send
758+
// this error to the client as well, but it is good to log the
759+
// failure early here and include the request number.
760+
MP_LOG(*server.m_context.loop, Log::Error) << "IPC server error request #" << req << " " << TypeName<Results>()
761+
<< " " << kj::str("kj::Exception: ", e).cStr();
762+
return kj::mv(e);
756763
});
757764
} catch (const std::exception& e) {
758765
MP_LOG(*server.m_context.loop, Log::Error) << "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
@@ -205,6 +205,40 @@ auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn&
205205
<< "IPC server error request #" << req << ", missing thread to execute request";
206206
throw std::runtime_error("invalid thread handle");
207207
}
208+
}, [&server, req](::kj::Exception&& e) -> kj::Promise<typename ServerContext::CallContext> {
209+
// If you see the error "(remote):0: failed: remote exception:
210+
// Called null capability" here, it probably means your Init class
211+
// is missing a declaration like:
212+
//
213+
// construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
214+
//
215+
// which passes a ThreadMap reference from the client to the server,
216+
// allowing the server to create threads to run IPC calls on the
217+
// client, and also returns a ThreadMap reference from the server to
218+
// the client, allowing the client to create threads on the server.
219+
// (Typically the latter ThreadMap is used more often because there
220+
// are more client-to-server calls.)
221+
//
222+
// If the other side of the connection did not previously get a
223+
// ThreadMap reference from this side of the connection, when the
224+
// other side calls `m_thread_map.makeThreadRequest()` in
225+
// `BuildField` above, `m_thread_map` will be null, but that call
226+
// will not fail immediately due to Cap'n Proto's request pipelining
227+
// and delayed execution. Instead that call will return an invalid
228+
// Thread reference, and when that reference is passed to this side
229+
// of the connection as `thread_client` above, the
230+
// `getLocalServer(thread_client)` call there will be the first
231+
// thing to overtly fail, leading to an error here.
232+
//
233+
// Potentially there are also other things that could cause errors
234+
// here, but this is the most likely cause.
235+
//
236+
// The log statement here is not strictly necessary since the same
237+
// exception will also be logged in serverInvoke, but this logging
238+
// may provide extra context that could be helpful for debugging.
239+
MP_LOG(*server.m_context.loop, Log::Info)
240+
<< "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?";
241+
return kj::mv(e);
208242
});
209243
// Use connection m_canceler object to cancel the result promise if the
210244
// connection is destroyed. (By default Cap'n Proto does not cancel requests

0 commit comments

Comments
 (0)