Compare commits

...
4 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås f67d1e81df Dump alive tasks every 30s in server and agent
Route the dump through GLib.info() using the new
g_task_describe_alive_tasks(), so the agent's dump reaches
logcat where its stdio is not visible. Bump the GLib wrap to
the branch holding that function.
2026-07-29 18:12:10 +02:00
Ole André Vadla Ravnås a068c704d1 Trace agent session detach notification path
Log every hop that agent_session_detached has to survive, from
the agent emitting closed, through frida-server's provider and
control channel, the droidy relay, and finally Device's pending
detach request. Each silent id-keyed drop now reports whether
the lookup succeeded, so a hanging detach() points straight at
the hop that swallowed the notification.
2026-07-29 17:47:25 +02:00
Ole André Vadla Ravnås 34ac3a0d50 server: Add output redirection option
Add -o/--output for redirecting stdout and stderr to a file,
so output survives daemonizing. Skip the stdin debug console
when daemonized, as stdin is then /dev/null.
2026-07-29 15:10:37 +02:00
Ole André Vadla Ravnås 1ea81bcd59 server: Add stdin-based task debug console 2026-07-28 14:47:05 +02:00
9 changed files with 146 additions and 13 deletions

No files matched your search

+18
View File
@@ -292,6 +292,8 @@ namespace Frida.Agent {
start.begin ((owned) padder);
start_alive_task_dumper ();
main_loop.run ();
main_context.pop_thread_default ();
@@ -362,6 +364,16 @@ namespace Frida.Agent {
padder = null;
}
// TEMPORARY DEBUG HACK: dump alive GTasks every 30 seconds.
private void start_alive_task_dumper () {
var source = new TimeoutSource.seconds (30);
source.set_callback (() => {
GLib.info ("%s", g_task_describe_alive_tasks ());
return Source.CONTINUE;
});
source.attach (main_context);
}
private void keep_running_eternalized () {
agent_gthread = new Thread<bool> ("frida-eternal-agent", () => {
var ignore_scope = new ThreadIgnoreScope (FRIDA_THREAD);
@@ -924,6 +936,8 @@ namespace Frida.Agent {
private void on_session_closed (BaseAgentSession base_session) {
LiveAgentSession session = (LiveAgentSession) base_session;
GLib.info ("detach-trace: Agent emitting provider closed id=%s", session.id.handle);
closed (session.id);
unregister_session (session);
@@ -1724,4 +1738,8 @@ namespace Frida.Agent {
return repeat;
});
}
// TEMPORARY DEBUG HACK: only present when GLib is built with G_ENABLE_DEBUG.
[CCode (cname = "g_task_describe_alive_tasks")]
private extern string g_task_describe_alive_tasks ();
}
+3
View File
@@ -361,6 +361,8 @@ namespace Frida {
private async void teardown_peer_connection_and_emit_closed () {
schedule_on_frida_thread (() => {
GLib.info ("detach-trace: AgentMessageTransmitter %p emitting closed, nice_agent=%p",
this, nice_agent);
if (nice_agent != null)
close_nice_resources_and_emit_closed.begin ();
else
@@ -598,6 +600,7 @@ namespace Frida {
private async void teardown_peer_connection_and_emit_closed () {
schedule_on_frida_thread (() => {
GLib.info ("detach-trace: AgentMessageTransmitter %p emitting closed", this);
closed ();
return Source.REMOVE;
});
+6
View File
@@ -57,6 +57,8 @@ namespace Frida {
}
public async void close (Cancellable? cancellable) throws IOError {
GLib.info ("detach-trace: BaseAgentSession.close() id=%s close_request=%p", id.handle, close_request);
while (close_request != null) {
try {
yield close_request.future.wait_async (cancellable);
@@ -85,6 +87,8 @@ namespace Frida {
yield transmitter.close (cancellable);
GLib.info ("detach-trace: BaseAgentSession.close() done id=%s", id.handle);
close_request.resolve (true);
}
@@ -276,6 +280,8 @@ namespace Frida {
transmitter.new_candidates.disconnect (on_transmitter_new_candidates);
transmitter.candidate_gathering_done.disconnect (on_transmitter_candidate_gathering_done);
GLib.info ("detach-trace: BaseAgentSession emitting closed id=%s", id.handle);
closed ();
}
+87 -4
View File
@@ -12,6 +12,7 @@ namespace Frida.Server {
private static string? directory = null;
#if !WINDOWS && !TVOS
private static bool daemonize = false;
private static string? output_path = null;
#endif
private static string? softener_flavor_str = null;
private static bool enable_preload = true;
@@ -42,6 +43,7 @@ namespace Frida.Server {
{ "directory", 'd', 0, OptionArg.STRING, ref directory, "Store binaries in DIRECTORY", "DIRECTORY" },
#if !WINDOWS && !TVOS
{ "daemonize", 'D', 0, OptionArg.NONE, ref daemonize, "Detach and become a daemon", null },
{ "output", 'o', 0, OptionArg.FILENAME, ref output_path, "Redirect output to FILE", "FILE" },
#endif
{ "policy-softener", 0, 0, OptionArg.STRING, ref softener_flavor_str, "Select policy softener", "system|internal" },
{ "disable-preload", 'P', OptionFlags.REVERSE, OptionArg.NONE, ref enable_preload, "Disable preload optimization", null },
@@ -78,6 +80,13 @@ namespace Frida.Server {
Environment.set_verbose_logging_enabled (verbose);
#if !WINDOWS && !TVOS
if (output_path != null && !daemonize) {
if (!redirect_output_to (output_path))
return 6;
}
#endif
EndpointParameters endpoint_params;
try {
endpoint_params = new EndpointParameters (listen_address, 0, parse_certificate (certpath), origin,
@@ -156,12 +165,15 @@ namespace Frida.Server {
Posix.setsid ();
var null_in = Posix.open ("/dev/null", Posix.O_RDONLY);
var null_out = Posix.open ("/dev/null", Posix.O_WRONLY);
Posix.dup2 (null_in, Posix.STDIN_FILENO);
Posix.dup2 (null_out, Posix.STDOUT_FILENO);
Posix.dup2 (null_out, Posix.STDERR_FILENO);
Posix.close (null_in);
Posix.close (null_out);
if (output_path == null || !redirect_output_to (output_path)) {
var null_out = Posix.open ("/dev/null", Posix.O_WRONLY);
Posix.dup2 (null_out, Posix.STDOUT_FILENO);
Posix.dup2 (null_out, Posix.STDERR_FILENO);
Posix.close (null_out);
}
}
var status = new uint8[1];
@@ -218,6 +230,22 @@ namespace Frida.Server {
return application.run ();
}
#if !WINDOWS && !TVOS
private static bool redirect_output_to (string path) {
int fd = Posix.open (path, Posix.O_WRONLY | Posix.O_CREAT | Posix.O_APPEND, 0644);
if (fd == -1) {
printerr ("Unable to open “%s”: %s\n", path, Posix.strerror (Posix.errno));
return false;
}
Posix.dup2 (fd, Posix.STDOUT_FILENO);
Posix.dup2 (fd, Posix.STDERR_FILENO);
Posix.close (fd);
return true;
}
#endif
namespace Environment {
public extern void init ();
public extern void set_verbose_logging_enabled (bool enabled);
@@ -274,6 +302,15 @@ namespace Frida.Server {
return false;
});
#if !WINDOWS && !TVOS
if (!daemonize)
process_commands.begin ();
#elif !WINDOWS
process_commands.begin ();
#endif
start_alive_task_dumper ();
exit_code = 0;
loop.run ();
@@ -281,6 +318,45 @@ namespace Frida.Server {
return exit_code;
}
#if !WINDOWS
// TEMPORARY DEBUG HACK: type “t” + Enter to dump alive GTasks.
private async void process_commands () {
var input = new DataInputStream (new UnixInputStream (Posix.STDIN_FILENO, false));
while (true) {
string? line;
try {
line = yield input.read_line_async (Priority.DEFAULT, io_cancellable);
} catch (GLib.Error e) {
return;
}
if (line == null)
return;
switch (line.strip ()) {
case "t":
g_task_print_alive_tasks ();
break;
case "":
break;
default:
printerr ("Unknown command; try “t”\n");
break;
}
}
}
#endif
// TEMPORARY DEBUG HACK: dump alive GTasks every 30 seconds.
private void start_alive_task_dumper () {
var source = new TimeoutSource.seconds (30);
source.set_callback (() => {
GLib.info ("%s", g_task_describe_alive_tasks ());
return Source.CONTINUE;
});
source.attach (MainContext.default ());
}
private async void start () {
try {
if (device_id != null && device_id != "local") {
@@ -355,4 +431,11 @@ namespace Frida.Server {
return new TlsCertificate.from_file (path);
}
// TEMPORARY DEBUG HACK: only present when GLib is built with G_ENABLE_DEBUG.
[CCode (cname = "g_task_print_alive_tasks")]
private extern void g_task_print_alive_tasks ();
[CCode (cname = "g_task_describe_alive_tasks")]
private extern string g_task_describe_alive_tasks ();
}
+7 -6
View File
@@ -762,12 +762,13 @@ namespace Frida {
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
AgentSessionEntry entry;
if (agent_sessions.unset (id, out entry)) {
ControlChannel? controller = entry.controller;
if (controller != null) {
controller.agent_sessions.remove (id);
controller.agent_session_detached (id, reason, crash);
}
bool entry_found = agent_sessions.unset (id, out entry);
ControlChannel? controller = entry_found ? entry.controller : null;
GLib.info ("detach-trace: ControlService session detached id=%s reason=%d entry_found=%s controller=%p",
id.handle, reason, entry_found.to_string (), controller);
if (controller != null) {
controller.agent_sessions.remove (id);
controller.agent_session_detached (id, reason, crash);
}
}
+5 -1
View File
@@ -993,7 +993,11 @@ namespace Frida {
private void on_remote_agent_session_detached (AgentSessionId remote_id, SessionDetachReason reason, CrashInfo crash) {
AgentSessionId? local_id;
if (!remote_agent_sessions.unset (remote_id, out local_id))
bool mapping_found = remote_agent_sessions.unset (remote_id, out local_id);
GLib.info ("detach-trace: Droidy remote session detached remote_id=%s reason=%d mapping_found=%s local_id=%s",
remote_id.handle, reason, mapping_found.to_string (),
mapping_found ? local_id.handle : "-");
if (!mapping_found)
return;
bool agent_session_found = agent_sessions.unset (local_id);
+13
View File
@@ -1885,6 +1885,9 @@ namespace Frida {
break;
}
}
GLib.info ("detach-trace: Device._release_session id=%s may_block=%s",
(session_id != null) ? session_id.handle : "-", may_block.to_string ());
if (session_id == null)
return;
@@ -1900,11 +1903,16 @@ namespace Frida {
} catch (Error e) {
assert_not_reached ();
}
GLib.info ("detach-trace: Device._release_session resumed id=%s", session_id.handle);
}
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
var session = agent_sessions[id];
GLib.info ("detach-trace: Device session detached id=%s reason=%d session=%p pending=%s",
id.handle, reason, session, pending_detach_requests.has_key (id).to_string ());
if (session != null)
session._on_detached (reason, crash);
@@ -3837,15 +3845,20 @@ namespace Frida {
}
private async void close_session_and_peer_connection (Cancellable? cancellable) throws IOError {
GLib.info ("detach-trace: Session closing remote id=%s", id.handle);
try {
yield active_session.close (cancellable);
} catch (GLib.Error e) {
GLib.info ("detach-trace: Session remote close failed id=%s error=%s", id.handle, e.message);
if (e is IOError.CANCELLED) {
discard_peer_connection ();
return;
}
}
GLib.info ("detach-trace: Session remote close returned id=%s", id.handle);
yield teardown_peer_connection (cancellable);
}
+6 -1
View File
@@ -871,6 +871,8 @@ namespace Frida {
private void on_agent_session_provider_closed (AgentSessionId id) {
var closed_after_opening = agent_sessions.unset (id);
GLib.info ("detach-trace: BaseDBusHostSession provider closed id=%s closed_after_opening=%s",
id.handle, closed_after_opening.to_string ());
if (!closed_after_opening)
return;
var reason = SessionDetachReason.APPLICATION_REQUESTED;
@@ -973,7 +975,10 @@ namespace Frida {
var crash_info = (crash != null) ? crash : CrashInfo.empty ();
foreach (var id in entry.sessions) {
if (agent_sessions.unset (id))
bool still_registered = agent_sessions.unset (id);
GLib.info ("detach-trace: BaseDBusHostSession teardown id=%s reason=%d still_registered=%s",
id.handle, reason, still_registered.to_string ());
if (still_registered)
agent_session_detached (id, reason, crash_info);
}
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/glib.git
revision = 92d65736b198c2cf131f691dd83db0b25f7b02d2
revision = 90a8b77b3d4cdf0482eb398fb2000dcceaeb15f0
depth = 1
[provide]