diff --git a/DevTools/Inspector/RemoteProcess.cpp b/DevTools/Inspector/RemoteProcess.cpp index f10630442a..e75b62aed8 100644 --- a/DevTools/Inspector/RemoteProcess.cpp +++ b/DevTools/Inspector/RemoteProcess.cpp @@ -11,10 +11,80 @@ RemoteProcess::RemoteProcess(pid_t pid) { } +void RemoteProcess::handle_identify_response(const JsonObject& response) +{ + int pid = response.get("pid").to_int(); + ASSERT(pid == m_pid); + + m_process_name = response.get("process_name").as_string_or({}); + + if (on_update) + on_update(); +} + +void RemoteProcess::handle_get_all_objects_response(const JsonObject& response) +{ + // FIXME: It would be good if we didn't have to make a local copy of the array value here! + auto objects = response.get("objects"); + auto& object_array = objects.as_array(); + + NonnullOwnPtrVector remote_objects; + HashMap objects_by_address; + + for (auto& value : object_array.values()) { + ASSERT(value.is_object()); + auto& object = value.as_object(); + auto remote_object = make(); + remote_object->address = object.get("address").to_string(); + remote_object->parent_address = object.get("parent").to_string(); + remote_object->name = object.get("name").to_string(); + remote_object->class_name = object.get("class_name").to_string(); + remote_object->json = object; + objects_by_address.set(remote_object->address, remote_object); + remote_objects.append(move(remote_object)); + } + + for (int i = 0; i < remote_objects.size(); ++i) { + auto& remote_object = remote_objects.ptr_at(i); + auto* parent = objects_by_address.get(remote_object->parent_address).value_or(nullptr); + if (!parent) { + m_roots.append(move(remote_object)); + } else { + remote_object->parent = parent; + parent->children.append(move(remote_object)); + } + } + + m_object_graph_model->update(); + + if (on_update) + on_update(); +} + +void RemoteProcess::send_request(const JsonObject& request) +{ + auto serialized = request.to_string(); + i32 length = serialized.length(); + m_socket.write((const u8*)&length, sizeof(length)); + m_socket.write(serialized); +} + void RemoteProcess::update() { m_socket.on_connected = [this] { dbg() << "Connected to PID " << m_pid; + + { + JsonObject request; + request.set("type", "Identify"); + send_request(request); + } + + { + JsonObject request; + request.set("type", "GetAllObjects"); + send_request(request); + } }; m_socket.on_ready_to_read = [this] { @@ -24,39 +94,35 @@ void RemoteProcess::update() return; } - auto data = m_socket.read_all(); + i32 length; + int nread = m_socket.read((u8*)&length, sizeof(length)); + ASSERT(nread == sizeof(length)); + + auto data = m_socket.read(length); + ASSERT(data.size() == length); + + dbg() << "Got packet size " << length << " and read that many bytes"; + auto json_value = JsonValue::from_string(data); - ASSERT(json_value.is_array()); - auto& object_array = json_value.as_array(); + ASSERT(json_value.is_object()); - Vector> remote_objects; - HashMap objects_by_address; + dbg() << "Got JSON response " << json_value.to_string(); - for (auto& value : object_array.values()) { - ASSERT(value.is_object()); - auto& object = value.as_object(); - auto remote_object = make(); - remote_object->address = object.get("address").to_string(); - remote_object->parent_address = object.get("parent").to_string(); - remote_object->name = object.get("name").to_string(); - remote_object->class_name = object.get("class_name").to_string(); - remote_object->json = object; - objects_by_address.set(remote_object->address, remote_object); - remote_objects.append(move(remote_object)); + auto& response = json_value.as_object(); + + auto response_type = response.get("type").as_string_or({}); + if (response_type.is_null()) + return; + + if (response_type == "GetAllObjects") { + handle_get_all_objects_response(response); + return; } - for (int i = 0; i < remote_objects.size(); ++i) { - auto& remote_object = remote_objects[i]; - auto* parent = objects_by_address.get(remote_object->parent_address).value_or(nullptr); - if (!parent) { - m_roots.append(move(remote_object)); - } else { - remote_object->parent = parent; - parent->children.append(move(remote_object)); - } + if (response_type == "Identify") { + handle_identify_response(response); + return; } - - m_object_graph_model->update(); }; auto success = m_socket.connect(CSocketAddress::local(String::format("/tmp/rpc.%d", m_pid))); diff --git a/DevTools/Inspector/RemoteProcess.h b/DevTools/Inspector/RemoteProcess.h index 8b37242955..ac6fa20440 100644 --- a/DevTools/Inspector/RemoteProcess.h +++ b/DevTools/Inspector/RemoteProcess.h @@ -3,6 +3,10 @@ #include #include +namespace AK { +class JsonObject; +} + class RemoteObjectGraphModel; class RemoteObject; @@ -11,11 +15,22 @@ public: explicit RemoteProcess(pid_t); void update(); + pid_t pid() const { return m_pid; } + const String& process_name() const { return m_process_name; } + RemoteObjectGraphModel& object_graph_model() { return *m_object_graph_model; } const NonnullOwnPtrVector& roots() const { return m_roots; } + Function on_update; + private: + void handle_get_all_objects_response(const AK::JsonObject&); + void handle_identify_response(const AK::JsonObject&); + + void send_request(const AK::JsonObject&); + pid_t m_pid { -1 }; + String m_process_name; NonnullRefPtr m_object_graph_model; CLocalSocket m_socket; NonnullOwnPtrVector m_roots; diff --git a/DevTools/Inspector/main.cpp b/DevTools/Inspector/main.cpp index 0e57e39951..4fc29779ab 100644 --- a/DevTools/Inspector/main.cpp +++ b/DevTools/Inspector/main.cpp @@ -41,6 +41,11 @@ int main(int argc, char** argv) RemoteProcess remote_process(pid); + remote_process.on_update = [&] { + if (!remote_process.process_name().is_null()) + window->set_title(String::format("Inspector: %s (%d)", remote_process.process_name().characters(), remote_process.pid())); + }; + auto* tree_view = new GTreeView(splitter); tree_view->set_model(remote_process.object_graph_model()); tree_view->set_activates_on_selection(true); diff --git a/Libraries/LibCore/CEventLoop.cpp b/Libraries/LibCore/CEventLoop.cpp index 1d34d7299d..e39f2a06fc 100644 --- a/Libraries/LibCore/CEventLoop.cpp +++ b/Libraries/LibCore/CEventLoop.cpp @@ -31,6 +31,96 @@ int CEventLoop::s_next_timer_id = 1; int CEventLoop::s_wake_pipe_fds[2]; CLocalServer CEventLoop::s_rpc_server; +class RPCClient : public CObject { + C_OBJECT(RPCClient) +public: + explicit RPCClient(CLocalSocket& socket) + : m_socket(socket) + { + add_child(socket); + + m_socket.on_ready_to_read = [this] { + i32 length; + int nread = m_socket.read((u8*)&length, sizeof(length)); + if (nread == 0) { + dbg() << "RPC client disconnected"; + delete_later(); + return; + } + ASSERT(nread == sizeof(length)); + auto request = m_socket.read(length); + + auto request_json = JsonValue::from_string(request); + if (!request_json.is_object()) { + dbg() << "RPC client sent invalid request"; + delete_later(); + return; + } + + handle_request(request_json.as_object()); + }; + } + virtual ~RPCClient() override + { + } + + void send_response(const JsonObject& response) + { + auto serialized = response.to_string(); + i32 length = serialized.length(); + m_socket.write((const u8*)&length, sizeof(length)); + m_socket.write(serialized); + } + + void handle_request(const JsonObject& request) + { + auto type = request.get("type").as_string_or({}); + + if (type.is_null()) { + dbg() << "RPC client sent request without type field"; + return; + } + + if (type == "Identify") { + JsonObject response; + response.set("type", type); + response.set("pid", getpid()); +#ifdef __serenity__ + char buffer[1024]; + if (get_process_name(buffer, sizeof(buffer)) >= 0) { + response.set("process_name", buffer); + } else { + response.set("process_name", JsonValue()); + } +#endif + send_response(response); + return; + } + + if (type == "GetAllObjects") { + JsonObject response; + response.set("type", type); + JsonArray objects; + for (auto& object : CObject::all_objects()) { + JsonObject json_object; + object.save_to(json_object); + objects.append(move(json_object)); + } + response.set("objects", move(objects)); + send_response(response); + return; + } + + if (type == "Disconnect") { + delete_later(); + return; + } + } + +private: + CLocalSocket& m_socket; +}; + CEventLoop::CEventLoop() { if (!s_event_loop_stack) { @@ -45,7 +135,6 @@ CEventLoop::CEventLoop() ASSERT(rc == 0); s_event_loop_stack->append(this); - auto rpc_path = String::format("/tmp/rpc.%d", getpid()); rc = unlink(rpc_path.characters()); if (rc < 0 && errno != ENOENT) { @@ -56,16 +145,9 @@ CEventLoop::CEventLoop() ASSERT(listening); s_rpc_server.on_ready_to_accept = [&] { - auto* client = s_rpc_server.accept(); - ASSERT(client); - JsonArray objects; - for (auto& object : CObject::all_objects()) { - JsonObject json_object; - object.save_to(json_object); - objects.append(move(json_object)); - } - client->write(objects.to_string()); - client->delete_later(); + auto* client_socket = s_rpc_server.accept(); + ASSERT(client_socket); + new RPCClient(*client_socket); }; } diff --git a/Userland/rpcdump.cpp b/Userland/rpcdump.cpp index 3e2cdd8b87..59a83ec6e9 100644 --- a/Userland/rpcdump.cpp +++ b/Userland/rpcdump.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -18,6 +20,13 @@ int main(int argc, char** argv) socket.on_connected = [&] { dbg() << "Connected to PID " << pid; + + JsonObject request; + request.set("type", "GetAllObjects"); + auto serialized = request.to_string(); + i32 length = serialized.length(); + socket.write((const u8*)&length, sizeof(length)); + socket.write(serialized); }; socket.on_ready_to_read = [&] { @@ -32,6 +41,8 @@ int main(int argc, char** argv) for (int i = 0; i < data.size(); ++i) putchar(data[i]); printf("\n"); + + loop.quit(0); }; auto success = socket.connect(CSocketAddress::local(String::format("/tmp/rpc.%d", pid)));