mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-11 13:36:13 +03:00
Userland: Remove "Inspector" program and related utilities
This program has never lived up to its original idea, and has been broken for years (property editing, etc). It's also unmaintained and off-by-default since forever. At this point, Inspector is more of a maintenance burden than a feature, so this commit removes it from the system, along with the mechanism in Core::EventLoop that enables it. If we decide we want the feature again in the future, it can be reimplemented better. :^)
This commit is contained in:
parent
203e84c378
commit
c756e021a7
Notes:
sideshowbarker
2024-07-17 09:47:09 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/c756e021a7 Pull-request: https://github.com/SerenityOS/serenity/pull/18493 Reviewed-by: https://github.com/FireFox317
@ -11,7 +11,6 @@ alias iv=ImageViewer
|
||||
alias pi=Piano
|
||||
alias calc=Calculator
|
||||
alias calendar=Calendar
|
||||
alias ins=Inspector
|
||||
alias sp=SoundPlayer
|
||||
alias help=Help
|
||||
alias br=Browser
|
||||
|
@ -11,7 +11,6 @@ alias iv=ImageViewer
|
||||
alias pi=Piano
|
||||
alias calc=Calculator
|
||||
alias calendar=Calendar
|
||||
alias ins=Inspector
|
||||
alias sp=SoundPlayer
|
||||
alias help=Help
|
||||
alias br=Browser
|
||||
|
@ -57,11 +57,6 @@ Lazy=true
|
||||
Priority=low
|
||||
KeepAlive=true
|
||||
|
||||
[InspectorServer]
|
||||
Socket=/tmp/session/%sid/portal/inspector,/tmp/session/%sid/portal/inspectables
|
||||
SocketPermissions=600,666
|
||||
KeepAlive=true
|
||||
|
||||
[AudioServer]
|
||||
Socket=/tmp/session/%sid/portal/audio
|
||||
Priority=high
|
||||
|
@ -23,6 +23,5 @@ Holding Ctrl accelerates mouse wheel interaction with sliders and spin boxes.
|
||||
Selected files can be renamed by pressing F2.
|
||||
Assistant can help you quickly find files and applications by pressing Super+Space.
|
||||
Holding Ctrl while activating a menu item prevents that menu from closing.
|
||||
Applications can be viewed with Inspector by including 'MAKE_INSPECTABLE=1' in their environment.
|
||||
Pressing Ctrl+Shift+A on a focused widget or application activates the command palette, a searchable list of available actions.
|
||||
Workspaces can be switched by pressing Ctrl+Alt+Arrows. Shift brings the active window along.
|
||||
|
@ -1,4 +0,0 @@
|
||||
[App]
|
||||
Name=Inspector
|
||||
Executable=/bin/Inspector
|
||||
Category=Development
|
@ -26,7 +26,6 @@ Note that many applications can be disabled at SerenityOS build time if desired.
|
||||
- [GML Playground](help://man/1/Applications/GMLPlayground)
|
||||
- [Hex Editor](help://man/1/Applications/HexEditor)
|
||||
- [Image Viewer](help://man/1/Applications/ImageViewer)
|
||||
- [Inspector](help://man/1/Applications/Inspector)
|
||||
- [Magnifier](help://man/1/Applications/Magnifier)
|
||||
- [Mail](help://man/1/Applications/Mail)
|
||||
- [Mouse Settings](help://man/1/Applications/MouseSettings)
|
||||
|
@ -1,31 +0,0 @@
|
||||
## Name
|
||||
|
||||
![Icon](/res/icons/16x16/app-inspector.png) Inspector - Serenity process inspector
|
||||
|
||||
[Open](file:///bin/Inspector)
|
||||
|
||||
## Synopsis
|
||||
|
||||
```**sh
|
||||
$ Inspector [pid]
|
||||
```
|
||||
|
||||
## Arguments
|
||||
|
||||
* `pid`: Process ID to inspect
|
||||
|
||||
## Description
|
||||
|
||||
Inspector facilitates process inspection via RPC.
|
||||
|
||||
To inspect a process, it must have `MAKE_INSPECTABLE=1` in its environment,
|
||||
and it must have previously allowed the
|
||||
[`accept`(2)](help://man/2/accept) system call with
|
||||
[`pledge`(2)](help://man/2/pledge) to allow inspection
|
||||
via UNIX socket.
|
||||
|
||||
## Examples
|
||||
|
||||
```sh
|
||||
$ Inspector $(pidof Shell)
|
||||
```
|
@ -41,8 +41,6 @@ $ watch -n 1 -- bt 124
|
||||
|
||||
## See also
|
||||
|
||||
* [`Inspector`(1)](help://man/1/Applications/Inspector)
|
||||
|
||||
* [`Profiler`(1)](help://man/1/Applications/Profiler)
|
||||
|
||||
* [`watch`(1)](help://man/1/watch)
|
||||
|
@ -52,11 +52,6 @@ This is a list of useful tips and tricks to help you make the most out of Sereni
|
||||
|
||||
## Development
|
||||
* Supplying `# profile` with a process identifier (PID) of `-1` as root enables systemwide profiling.
|
||||
* Make applications inspectable by including `MAKE_INSPECTABLE=1` in their environment. Inspectable processes can be examined with [Inspector](help://man/1/Applications/Inspector) via remote procedure calls (RPCs). For example, to make the Eyes application inspectable, enter the following Shell commands:
|
||||
```sh
|
||||
$ export MAKE_INSPECTABLE=1
|
||||
$ Eyes &
|
||||
```
|
||||
|
||||
## See also
|
||||
* [Keyboard Shortcuts](help://man/7/KeyboardShortcuts)
|
||||
* [Keyboard Shortcuts](help://man/7/KeyboardShortcuts)
|
||||
|
@ -300,7 +300,7 @@ index 0000000000000000000000000000000000000000..8a5abd58967dee12619c45d0e0d6cfc8
|
||||
+
|
||||
+ // We need to create our audio connection and event loop here, in order to register them with SDL's audio thread
|
||||
+ if (!h->event_loop)
|
||||
+ h->event_loop = make<Core::EventLoop>(Core::EventLoop::MakeInspectable::No);
|
||||
+ h->event_loop = make<Core::EventLoop>();
|
||||
+ if (!h->client)
|
||||
+ h->client = MUST(Audio::ConnectionToServer::try_create());
|
||||
+
|
||||
|
@ -1,7 +1,7 @@
|
||||
serenity_component(
|
||||
SystemMonitor
|
||||
REQUIRED
|
||||
TARGETS SystemMonitor Profiler Inspector
|
||||
TARGETS SystemMonitor Profiler
|
||||
)
|
||||
|
||||
compile_gml(SystemMonitor.gml SystemMonitorGML.h system_monitor_gml)
|
||||
|
@ -263,7 +263,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
return result.release_error();
|
||||
|
||||
TRY(Core::System::unveil("/bin/Profiler", "rx"));
|
||||
TRY(Core::System::unveil("/bin/Inspector", "rx"));
|
||||
TRY(Core::System::unveil("/bin/HackStudio", "rx"));
|
||||
TRY(Core::System::unveil(nullptr, nullptr));
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
TRY(Core::System::pledge("stdio recvfd sendfd rpath unix thread"));
|
||||
auto app = TRY(GUI::Application::try_create(arguments, Core::EventLoop::MakeInspectable::Yes));
|
||||
auto app = TRY(GUI::Application::try_create(arguments));
|
||||
|
||||
TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw"));
|
||||
TRY(Core::System::unveil("/res", "r"));
|
||||
|
@ -1,5 +1,4 @@
|
||||
add_subdirectory(GMLPlayground)
|
||||
add_subdirectory(Inspector)
|
||||
add_subdirectory(Profiler)
|
||||
add_subdirectory(HackStudio)
|
||||
add_subdirectory(SQLStudio)
|
||||
|
@ -1,16 +0,0 @@
|
||||
serenity_component(
|
||||
Inspector
|
||||
RECOMMENDED
|
||||
TARGETS Inspector
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
RemoteObject.cpp
|
||||
RemoteObjectGraphModel.cpp
|
||||
RemoteObjectPropertyModel.cpp
|
||||
RemoteProcess.cpp
|
||||
)
|
||||
|
||||
serenity_app(Inspector ICON app-inspector)
|
||||
target_link_libraries(Inspector PRIVATE LibCore LibDesktop LibGfx LibGUI LibIPC LibMain)
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <InspectorServer/InspectorClientEndpoint.h>
|
||||
#include <InspectorServer/InspectorServerEndpoint.h>
|
||||
#include <LibIPC/ConnectionToServer.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class InspectorServerClient final
|
||||
: public IPC::ConnectionToServer<InspectorClientEndpoint, InspectorServerEndpoint>
|
||||
, public InspectorClientEndpoint {
|
||||
IPC_CLIENT_CONNECTION(InspectorServerClient, "/tmp/session/%sid/portal/inspector"sv)
|
||||
|
||||
public:
|
||||
virtual ~InspectorServerClient() override = default;
|
||||
|
||||
private:
|
||||
InspectorServerClient(NonnullOwnPtr<Core::LocalSocket> socket)
|
||||
: IPC::ConnectionToServer<InspectorClientEndpoint, InspectorServerEndpoint>(*this, move(socket))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteObjectPropertyModel.h"
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
RemoteObject::RemoteObject()
|
||||
: m_property_model(RemoteObjectPropertyModel::create(*this))
|
||||
{
|
||||
}
|
||||
|
||||
RemoteObjectPropertyModel& RemoteObject::property_model()
|
||||
{
|
||||
m_property_model->invalidate();
|
||||
return *m_property_model;
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/DeprecatedString.h>
|
||||
#include <AK/JsonObject.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class RemoteObjectPropertyModel;
|
||||
|
||||
class RemoteObject {
|
||||
public:
|
||||
RemoteObject();
|
||||
|
||||
RemoteObjectPropertyModel& property_model();
|
||||
|
||||
RemoteObject* parent { nullptr };
|
||||
Vector<NonnullOwnPtr<RemoteObject>> children;
|
||||
|
||||
FlatPtr address { 0 };
|
||||
FlatPtr parent_address { 0 };
|
||||
DeprecatedString class_name;
|
||||
DeprecatedString name;
|
||||
|
||||
JsonObject json;
|
||||
|
||||
NonnullRefPtr<RemoteObjectPropertyModel> m_property_model;
|
||||
};
|
||||
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "RemoteObjectGraphModel.h"
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteProcess.h"
|
||||
#include <AK/JsonValue.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
RemoteObjectGraphModel::RemoteObjectGraphModel(RemoteProcess& process)
|
||||
: m_process(process)
|
||||
{
|
||||
m_object_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"sv).release_value_but_fixme_should_propagate_errors());
|
||||
m_window_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"sv).release_value_but_fixme_should_propagate_errors());
|
||||
m_layout_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/layout.png"sv).release_value_but_fixme_should_propagate_errors());
|
||||
m_timer_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/timer.png"sv).release_value_but_fixme_should_propagate_errors());
|
||||
}
|
||||
|
||||
GUI::ModelIndex RemoteObjectGraphModel::index(int row, int column, const GUI::ModelIndex& parent) const
|
||||
{
|
||||
if (!parent.is_valid()) {
|
||||
if (m_process.roots().is_empty())
|
||||
return {};
|
||||
return create_index(row, column, &m_process.roots().at(row));
|
||||
}
|
||||
auto& remote_parent = *static_cast<RemoteObject*>(parent.internal_data());
|
||||
return create_index(row, column, &remote_parent.children.at(row));
|
||||
}
|
||||
|
||||
GUI::ModelIndex RemoteObjectGraphModel::parent_index(const GUI::ModelIndex& index) const
|
||||
{
|
||||
if (!index.is_valid())
|
||||
return {};
|
||||
auto& remote_object = *static_cast<RemoteObject*>(index.internal_data());
|
||||
if (!remote_object.parent)
|
||||
return {};
|
||||
|
||||
// NOTE: If the parent has no parent, it's a root, so we have to look among the remote roots.
|
||||
if (!remote_object.parent->parent) {
|
||||
for (size_t row = 0; row < m_process.roots().size(); ++row) {
|
||||
if (m_process.roots()[row] == remote_object.parent)
|
||||
return create_index(row, 0, remote_object.parent);
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
for (size_t row = 0; row < remote_object.parent->parent->children.size(); ++row) {
|
||||
if (remote_object.parent->parent->children[row] == remote_object.parent)
|
||||
return create_index(row, 0, remote_object.parent);
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
int RemoteObjectGraphModel::row_count(const GUI::ModelIndex& index) const
|
||||
{
|
||||
if (!index.is_valid())
|
||||
return m_process.roots().size();
|
||||
auto& remote_object = *static_cast<RemoteObject*>(index.internal_data());
|
||||
return remote_object.children.size();
|
||||
}
|
||||
|
||||
int RemoteObjectGraphModel::column_count(const GUI::ModelIndex&) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
GUI::Variant RemoteObjectGraphModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
||||
{
|
||||
auto* remote_object = static_cast<RemoteObject*>(index.internal_data());
|
||||
if (role == GUI::ModelRole::Icon) {
|
||||
if (remote_object->class_name == "Window")
|
||||
return m_window_icon;
|
||||
if (remote_object->class_name == "Timer")
|
||||
return m_timer_icon;
|
||||
if (remote_object->class_name.ends_with("Layout"sv))
|
||||
return m_layout_icon;
|
||||
return m_object_icon;
|
||||
}
|
||||
if (role == GUI::ModelRole::Display)
|
||||
return DeprecatedString::formatted("{}({:p})", remote_object->class_name, remote_object->address);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <LibGUI/Model.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class RemoteProcess;
|
||||
|
||||
class RemoteObjectGraphModel final : public GUI::Model {
|
||||
public:
|
||||
static NonnullRefPtr<RemoteObjectGraphModel> create(RemoteProcess& process)
|
||||
{
|
||||
return adopt_ref(*new RemoteObjectGraphModel(process));
|
||||
}
|
||||
|
||||
virtual ~RemoteObjectGraphModel() override = default;
|
||||
|
||||
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||
virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
|
||||
virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
|
||||
virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
|
||||
|
||||
private:
|
||||
explicit RemoteObjectGraphModel(RemoteProcess&);
|
||||
|
||||
RemoteProcess& m_process;
|
||||
|
||||
GUI::Icon m_object_icon;
|
||||
GUI::Icon m_window_icon;
|
||||
GUI::Icon m_layout_icon;
|
||||
GUI::Icon m_timer_icon;
|
||||
};
|
||||
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "RemoteObjectPropertyModel.h"
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteProcess.h"
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object)
|
||||
: m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
int RemoteObjectPropertyModel::row_count(const GUI::ModelIndex& index) const
|
||||
{
|
||||
Function<int(JsonValue const&)> do_count = [&](JsonValue const& value) {
|
||||
if (value.is_array())
|
||||
return value.as_array().size();
|
||||
else if (value.is_object())
|
||||
return value.as_object().size();
|
||||
return (size_t)0;
|
||||
};
|
||||
|
||||
if (index.is_valid()) {
|
||||
auto* path = static_cast<JsonPath const*>(index.internal_data());
|
||||
return do_count(path->resolve(m_object.json));
|
||||
} else {
|
||||
return do_count(m_object.json);
|
||||
}
|
||||
}
|
||||
|
||||
DeprecatedString RemoteObjectPropertyModel::column_name(int column) const
|
||||
{
|
||||
switch (column) {
|
||||
case Column::Name:
|
||||
return "Name";
|
||||
case Column::Value:
|
||||
return "Value";
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
GUI::Variant RemoteObjectPropertyModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
||||
{
|
||||
auto* path = static_cast<JsonPath const*>(index.internal_data());
|
||||
if (!path)
|
||||
return {};
|
||||
|
||||
if (role == GUI::ModelRole::Display) {
|
||||
switch (index.column()) {
|
||||
case Column::Name:
|
||||
return path->last().to_deprecated_string();
|
||||
case Column::Value: {
|
||||
auto data = path->resolve(m_object.json);
|
||||
if (data.is_array())
|
||||
return DeprecatedString::formatted("<Array with {} element{}", data.as_array().size(), data.as_array().size() == 1 ? ">" : "s>");
|
||||
if (data.is_object())
|
||||
return DeprecatedString::formatted("<Object with {} entr{}", data.as_object().size(), data.as_object().size() == 1 ? "y>" : "ies>");
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void RemoteObjectPropertyModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& new_value)
|
||||
{
|
||||
if (!index.is_valid())
|
||||
return;
|
||||
|
||||
auto* path = static_cast<JsonPath const*>(index.internal_data());
|
||||
if (path->size() != 1)
|
||||
return;
|
||||
|
||||
FlatPtr address = m_object.address;
|
||||
RemoteProcess::the().set_property(address, path->first().to_deprecated_string(), new_value.to_deprecated_string());
|
||||
did_update();
|
||||
}
|
||||
|
||||
GUI::ModelIndex RemoteObjectPropertyModel::index(int row, int column, const GUI::ModelIndex& parent) const
|
||||
{
|
||||
auto const& parent_path = parent.is_valid() ? *static_cast<JsonPath const*>(parent.internal_data()) : JsonPath {};
|
||||
|
||||
auto nth_child = [&](int n, JsonValue const& value) -> JsonPath const* {
|
||||
auto path = make<JsonPath>();
|
||||
path->extend(parent_path);
|
||||
int row_index = n;
|
||||
if (value.is_object()) {
|
||||
DeprecatedString property_name;
|
||||
auto& object = value.as_object();
|
||||
object.for_each_member([&](auto& name, auto&) {
|
||||
if (row_index > 0) {
|
||||
--row_index;
|
||||
} else if (row_index == 0) {
|
||||
property_name = name;
|
||||
--row_index;
|
||||
}
|
||||
});
|
||||
if (property_name.is_null())
|
||||
return nullptr;
|
||||
|
||||
path->append({ property_name });
|
||||
m_paths.append(move(path));
|
||||
} else if (value.is_array()) {
|
||||
path->append(JsonPathElement { (size_t)n });
|
||||
m_paths.append(move(path));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
return m_paths.last();
|
||||
};
|
||||
|
||||
if (!parent.is_valid()) {
|
||||
if (m_object.json.is_empty())
|
||||
return {};
|
||||
}
|
||||
|
||||
auto index_path = cached_path_at(row, parent_path);
|
||||
|
||||
if (!index_path)
|
||||
index_path = nth_child(row, parent_path.resolve(m_object.json));
|
||||
|
||||
if (!index_path)
|
||||
return {};
|
||||
|
||||
return create_index(row, column, index_path);
|
||||
}
|
||||
|
||||
GUI::ModelIndex RemoteObjectPropertyModel::parent_index(const GUI::ModelIndex& index) const
|
||||
{
|
||||
if (!index.is_valid())
|
||||
return index;
|
||||
|
||||
auto path = *static_cast<JsonPath const*>(index.internal_data());
|
||||
if (path.is_empty())
|
||||
return {};
|
||||
|
||||
path.take_last();
|
||||
if (path.is_empty())
|
||||
return {};
|
||||
|
||||
auto* cpath = find_cached_path(path);
|
||||
if (cpath) {
|
||||
int index_in_parent = 0;
|
||||
if (cpath->last().kind() == JsonPathElement::Kind::Index)
|
||||
index_in_parent = cpath->last().index();
|
||||
else if (cpath->last().kind() == JsonPathElement::Kind::Key) {
|
||||
auto path_copy = path;
|
||||
auto last = path_copy.take_last();
|
||||
bool found = false;
|
||||
path_copy.resolve(m_object.json).as_object().for_each_member([&](auto& name, auto&) {
|
||||
if (!found) {
|
||||
if (last.key() == name)
|
||||
found = true;
|
||||
else
|
||||
index_in_parent++;
|
||||
}
|
||||
});
|
||||
}
|
||||
return create_index(index_in_parent, 0, cpath);
|
||||
}
|
||||
|
||||
dbgln("No cached path found for path {}", path.to_deprecated_string());
|
||||
return {};
|
||||
}
|
||||
|
||||
JsonPath const* RemoteObjectPropertyModel::cached_path_at(int n, Vector<JsonPathElement> const& prefix) const
|
||||
{
|
||||
// FIXME: ModelIndex wants a void*, so we have to keep these
|
||||
// indices alive, but allocating a new path every time
|
||||
// we're asked for an index is silly, so we have to look for existing ones first.
|
||||
JsonPath const* index_path = nullptr;
|
||||
int row_index = n;
|
||||
for (auto& path : m_paths) {
|
||||
if (path->size() != prefix.size() + 1)
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < prefix.size(); ++i) {
|
||||
if ((*path)[i] != prefix[i])
|
||||
goto do_continue;
|
||||
}
|
||||
|
||||
if (row_index == 0) {
|
||||
index_path = path;
|
||||
break;
|
||||
}
|
||||
--row_index;
|
||||
do_continue:;
|
||||
}
|
||||
|
||||
return index_path;
|
||||
};
|
||||
|
||||
JsonPath const* RemoteObjectPropertyModel::find_cached_path(Vector<JsonPathElement> const& path) const
|
||||
{
|
||||
for (auto& cpath : m_paths) {
|
||||
if (cpath->size() != path.size())
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < cpath->size(); ++i) {
|
||||
if ((*cpath)[i] != path[i])
|
||||
goto do_continue;
|
||||
}
|
||||
|
||||
return cpath;
|
||||
do_continue:;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonPath.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <LibGUI/Model.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class RemoteObject;
|
||||
|
||||
class RemoteObjectPropertyModel final : public GUI::Model {
|
||||
public:
|
||||
virtual ~RemoteObjectPropertyModel() override { }
|
||||
static NonnullRefPtr<RemoteObjectPropertyModel> create(RemoteObject& object)
|
||||
{
|
||||
return adopt_ref(*new RemoteObjectPropertyModel(object));
|
||||
}
|
||||
|
||||
enum Column {
|
||||
Name,
|
||||
Value,
|
||||
__Count,
|
||||
};
|
||||
|
||||
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||
virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; }
|
||||
virtual DeprecatedString column_name(int) const override;
|
||||
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
|
||||
virtual void set_data(const GUI::ModelIndex&, const GUI::Variant&) override;
|
||||
virtual bool is_editable(const GUI::ModelIndex& index) const override { return index.column() == Column::Value; }
|
||||
virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
|
||||
virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
|
||||
|
||||
private:
|
||||
explicit RemoteObjectPropertyModel(RemoteObject&);
|
||||
|
||||
JsonPath const* cached_path_at(int n, Vector<JsonPathElement> const& prefix) const;
|
||||
JsonPath const* find_cached_path(Vector<JsonPathElement> const& path) const;
|
||||
|
||||
RemoteObject& m_object;
|
||||
mutable Vector<NonnullOwnPtr<JsonPath>> m_paths;
|
||||
};
|
||||
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "RemoteProcess.h"
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteObjectGraphModel.h"
|
||||
#include "RemoteObjectPropertyModel.h"
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
RemoteProcess* s_the;
|
||||
|
||||
RemoteProcess& RemoteProcess::the()
|
||||
{
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
RemoteProcess::RemoteProcess(pid_t pid)
|
||||
: m_pid(pid)
|
||||
, m_object_graph_model(RemoteObjectGraphModel::create(*this))
|
||||
{
|
||||
s_the = this;
|
||||
m_client = InspectorServerClient::try_create().release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void RemoteProcess::handle_identify_response(JsonObject const& response)
|
||||
{
|
||||
int pid = response.get_i32("pid"sv).value_or(0);
|
||||
VERIFY(pid == m_pid);
|
||||
|
||||
m_process_name = response.get_deprecated_string("process_name"sv).value_or({});
|
||||
|
||||
if (on_update)
|
||||
on_update();
|
||||
}
|
||||
|
||||
void RemoteProcess::handle_get_all_objects_response(JsonObject const& response)
|
||||
{
|
||||
// FIXME: It would be good if we didn't have to make a local copy of the array value here!
|
||||
auto& object_array = response.get_array("objects"sv).value();
|
||||
|
||||
Vector<NonnullOwnPtr<RemoteObject>> remote_objects;
|
||||
HashMap<FlatPtr, RemoteObject*> objects_by_address;
|
||||
|
||||
for (auto& value : object_array.values()) {
|
||||
VERIFY(value.is_object());
|
||||
auto& object = value.as_object();
|
||||
auto remote_object = make<RemoteObject>();
|
||||
remote_object->address = object.get_addr("address"sv).value_or(0);
|
||||
remote_object->parent_address = object.get_addr("parent"sv).value_or(0);
|
||||
remote_object->name = object.get_deprecated_string("name"sv).value_or({});
|
||||
remote_object->class_name = object.get_deprecated_string("class_name"sv).value_or({});
|
||||
remote_object->json = object;
|
||||
objects_by_address.set(remote_object->address, remote_object);
|
||||
remote_objects.append(move(remote_object));
|
||||
}
|
||||
|
||||
for (size_t 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));
|
||||
}
|
||||
}
|
||||
|
||||
m_object_graph_model->invalidate();
|
||||
|
||||
if (on_update)
|
||||
on_update();
|
||||
}
|
||||
|
||||
void RemoteProcess::set_inspected_object(FlatPtr address)
|
||||
{
|
||||
m_client->async_set_inspected_object(m_pid, address);
|
||||
}
|
||||
|
||||
void RemoteProcess::set_property(FlatPtr object, StringView name, JsonValue const& value)
|
||||
{
|
||||
m_client->async_set_object_property(m_pid, object, name, value.to_deprecated_string());
|
||||
}
|
||||
|
||||
bool RemoteProcess::is_inspectable()
|
||||
{
|
||||
return m_client->is_inspectable(m_pid);
|
||||
}
|
||||
|
||||
void RemoteProcess::update()
|
||||
{
|
||||
{
|
||||
auto raw_json = m_client->identify(m_pid);
|
||||
auto json = JsonValue::from_string(raw_json);
|
||||
handle_identify_response(json.value().as_object());
|
||||
}
|
||||
|
||||
{
|
||||
auto raw_json = m_client->get_all_objects(m_pid);
|
||||
auto json = JsonValue::from_string(raw_json);
|
||||
handle_get_all_objects_response(json.value().as_object());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "InspectorServerClient.h"
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class RemoteObjectGraphModel;
|
||||
class RemoteObject;
|
||||
|
||||
class RemoteProcess {
|
||||
public:
|
||||
static RemoteProcess& the();
|
||||
|
||||
explicit RemoteProcess(pid_t);
|
||||
void update();
|
||||
|
||||
pid_t pid() const { return m_pid; }
|
||||
DeprecatedString const& process_name() const { return m_process_name; }
|
||||
|
||||
RemoteObjectGraphModel& object_graph_model() { return *m_object_graph_model; }
|
||||
Vector<NonnullOwnPtr<RemoteObject>> const& roots() const { return m_roots; }
|
||||
|
||||
void set_inspected_object(FlatPtr);
|
||||
|
||||
void set_property(FlatPtr object, StringView name, JsonValue const& value);
|
||||
|
||||
bool is_inspectable();
|
||||
|
||||
Function<void()> on_update;
|
||||
|
||||
private:
|
||||
void handle_get_all_objects_response(JsonObject const&);
|
||||
void handle_identify_response(JsonObject const&);
|
||||
|
||||
pid_t m_pid { -1 };
|
||||
DeprecatedString m_process_name;
|
||||
NonnullRefPtr<RemoteObjectGraphModel> m_object_graph_model;
|
||||
Vector<NonnullOwnPtr<RemoteObject>> m_roots;
|
||||
RefPtr<InspectorServerClient> m_client;
|
||||
};
|
||||
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteObjectGraphModel.h"
|
||||
#include "RemoteObjectPropertyModel.h"
|
||||
#include "RemoteProcess.h"
|
||||
#include <AK/URL.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibDesktop/Launcher.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/Clipboard.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Menubar.h>
|
||||
#include <LibGUI/MessageBox.h>
|
||||
#include <LibGUI/ModelEditingDelegate.h>
|
||||
#include <LibGUI/ProcessChooser.h>
|
||||
#include <LibGUI/Splitter.h>
|
||||
#include <LibGUI/TreeView.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibMain/Main.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace Inspector;
|
||||
|
||||
[[noreturn]] static void print_usage_and_exit()
|
||||
{
|
||||
outln("usage: Inspector <pid>");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
TRY(Core::System::pledge("stdio recvfd sendfd rpath unix"));
|
||||
TRY(Core::System::unveil("/res", "r"));
|
||||
TRY(Core::System::unveil("/bin", "r"));
|
||||
TRY(Core::System::unveil("/tmp", "rwc"));
|
||||
TRY(Core::System::unveil("/sys/kernel/processes", "r"));
|
||||
TRY(Core::System::unveil("/etc/passwd", "r"));
|
||||
TRY(Core::System::unveil(nullptr, nullptr));
|
||||
|
||||
bool gui_mode = arguments.argc != 2;
|
||||
pid_t pid;
|
||||
|
||||
auto app = TRY(GUI::Application::try_create(arguments));
|
||||
auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-inspector"sv));
|
||||
if (gui_mode) {
|
||||
choose_pid:
|
||||
auto process_chooser = TRY(GUI::ProcessChooser::try_create("Inspector"sv, "Inspect"_short_string, app_icon.bitmap_for_size(16)));
|
||||
if (process_chooser->exec() == GUI::Dialog::ExecResult::Cancel)
|
||||
return 0;
|
||||
pid = process_chooser->pid();
|
||||
} else {
|
||||
auto pid_opt = DeprecatedString(arguments.strings[1]).to_int();
|
||||
if (!pid_opt.has_value())
|
||||
print_usage_and_exit();
|
||||
pid = pid_opt.value();
|
||||
}
|
||||
|
||||
auto window = TRY(GUI::Window::try_create());
|
||||
|
||||
if (pid == getpid()) {
|
||||
GUI::MessageBox::show(window, "Cannot inspect Inspector itself!"sv, "Error"sv, GUI::MessageBox::Type::Error);
|
||||
if (gui_mode)
|
||||
goto choose_pid;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
RemoteProcess remote_process(pid);
|
||||
if (!remote_process.is_inspectable()) {
|
||||
GUI::MessageBox::show(window, DeprecatedString::formatted("Process pid={} is not inspectable", remote_process.pid()), "Error"sv, GUI::MessageBox::Type::Error);
|
||||
if (gui_mode) {
|
||||
goto choose_pid;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man1/Applications/Inspector.md") }));
|
||||
TRY(Desktop::Launcher::seal_allowlist());
|
||||
|
||||
window->set_title("Inspector");
|
||||
window->resize(685, 500);
|
||||
window->set_icon(app_icon.bitmap_for_size(16));
|
||||
|
||||
auto& file_menu = window->add_menu("&File"_short_string);
|
||||
file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); }));
|
||||
|
||||
auto& help_menu = window->add_menu("&Help"_short_string);
|
||||
help_menu.add_action(GUI::CommonActions::make_command_palette_action(window));
|
||||
help_menu.add_action(GUI::CommonActions::make_help_action([](auto&) {
|
||||
Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/Inspector.md"), "/bin/Help");
|
||||
}));
|
||||
help_menu.add_action(GUI::CommonActions::make_about_action("Inspector", app_icon, window));
|
||||
|
||||
auto widget = TRY(window->set_main_widget<GUI::Widget>());
|
||||
widget->set_fill_with_background_color(true);
|
||||
widget->set_layout<GUI::VerticalBoxLayout>();
|
||||
|
||||
auto& splitter = widget->add<GUI::HorizontalSplitter>();
|
||||
|
||||
remote_process.on_update = [&] {
|
||||
if (!remote_process.process_name().is_null())
|
||||
window->set_title(DeprecatedString::formatted("{} ({}) - Inspector", remote_process.process_name(), remote_process.pid()));
|
||||
};
|
||||
|
||||
auto& tree_view = splitter.add<GUI::TreeView>();
|
||||
tree_view.set_model(remote_process.object_graph_model());
|
||||
tree_view.set_activates_on_selection(true);
|
||||
tree_view.set_preferred_width(286);
|
||||
|
||||
auto& properties_tree_view = splitter.add<GUI::TreeView>();
|
||||
properties_tree_view.set_should_fill_selected_rows(true);
|
||||
properties_tree_view.set_editable(true);
|
||||
properties_tree_view.aid_create_editing_delegate = [](auto&) {
|
||||
return make<GUI::StringModelEditingDelegate>();
|
||||
};
|
||||
|
||||
tree_view.on_activation = [&](auto& index) {
|
||||
auto* remote_object = static_cast<RemoteObject*>(index.internal_data());
|
||||
properties_tree_view.set_model(remote_object->property_model());
|
||||
remote_process.set_inspected_object(remote_object->address);
|
||||
};
|
||||
|
||||
auto properties_tree_view_context_menu = TRY(GUI::Menu::try_create(TRY("Properties Tree View"_string)));
|
||||
|
||||
auto copy_bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/edit-copy.png"sv).release_value_but_fixme_should_propagate_errors();
|
||||
auto copy_property_name_action = GUI::Action::create("Copy Property Name", copy_bitmap, [&](auto&) {
|
||||
GUI::Clipboard::the().set_plain_text(properties_tree_view.selection().first().data().to_deprecated_string());
|
||||
});
|
||||
auto copy_property_value_action = GUI::Action::create("Copy Property Value", copy_bitmap, [&](auto&) {
|
||||
GUI::Clipboard::the().set_plain_text(properties_tree_view.selection().first().sibling_at_column(1).data().to_deprecated_string());
|
||||
});
|
||||
|
||||
properties_tree_view_context_menu->add_action(copy_property_name_action);
|
||||
properties_tree_view_context_menu->add_action(copy_property_value_action);
|
||||
|
||||
properties_tree_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) {
|
||||
if (index.is_valid()) {
|
||||
properties_tree_view_context_menu->popup(event.screen_position());
|
||||
}
|
||||
};
|
||||
|
||||
window->show();
|
||||
remote_process.update();
|
||||
|
||||
TRY(Core::System::pledge("stdio recvfd sendfd rpath"));
|
||||
return app->exec();
|
||||
}
|
@ -48,10 +48,6 @@ extern bool s_global_initializers_ran;
|
||||
|
||||
namespace Core {
|
||||
|
||||
class InspectorServerConnection;
|
||||
|
||||
[[maybe_unused]] static bool connect_to_inspector_server();
|
||||
|
||||
struct EventLoopTimer {
|
||||
int timer_id { 0 };
|
||||
Time interval;
|
||||
@ -74,7 +70,6 @@ struct EventLoop::Private {
|
||||
};
|
||||
|
||||
static Threading::MutexProtected<NeverDestroyed<IDAllocator>> s_id_allocator;
|
||||
static Threading::MutexProtected<RefPtr<InspectorServerConnection>> s_inspector_server_connection;
|
||||
|
||||
// Each thread has its own event loop stack, its own timers, notifiers and a wake pipe.
|
||||
static thread_local Vector<EventLoop&>* s_event_loop_stack;
|
||||
@ -162,145 +157,7 @@ inline SignalHandlersInfo* signals_info()
|
||||
|
||||
pid_t EventLoop::s_pid;
|
||||
|
||||
class InspectorServerConnection : public Object {
|
||||
C_OBJECT(InspectorServerConnection)
|
||||
private:
|
||||
explicit InspectorServerConnection(NonnullOwnPtr<LocalSocket> socket)
|
||||
: m_socket(move(socket))
|
||||
{
|
||||
#ifdef AK_OS_SERENITY
|
||||
m_socket->on_ready_to_read = [this] {
|
||||
u32 length;
|
||||
auto maybe_bytes_read = m_socket->read_some({ (u8*)&length, sizeof(length) });
|
||||
if (maybe_bytes_read.is_error()) {
|
||||
dbgln("InspectorServerConnection: Failed to read message length from inspector server connection: {}", maybe_bytes_read.error());
|
||||
return;
|
||||
}
|
||||
|
||||
auto bytes_read = maybe_bytes_read.release_value();
|
||||
if (bytes_read.is_empty()) {
|
||||
dbgln_if(EVENTLOOP_DEBUG, "RPC client disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
VERIFY(bytes_read.size() == sizeof(length));
|
||||
|
||||
auto request_buffer = ByteBuffer::create_uninitialized(length).release_value();
|
||||
maybe_bytes_read = m_socket->read_some(request_buffer.bytes());
|
||||
if (maybe_bytes_read.is_error()) {
|
||||
dbgln("InspectorServerConnection: Failed to read message content from inspector server connection: {}", maybe_bytes_read.error());
|
||||
return;
|
||||
}
|
||||
|
||||
bytes_read = maybe_bytes_read.release_value();
|
||||
|
||||
auto request_json = JsonValue::from_string(request_buffer);
|
||||
if (request_json.is_error() || !request_json.value().is_object()) {
|
||||
dbgln("RPC client sent invalid request");
|
||||
return;
|
||||
}
|
||||
|
||||
handle_request(request_json.value().as_object());
|
||||
};
|
||||
#else
|
||||
warnln("RPC Client constructed outside serenity, this is very likely a bug!");
|
||||
#endif
|
||||
}
|
||||
virtual ~InspectorServerConnection() override
|
||||
{
|
||||
if (auto inspected_object = m_inspected_object.strong_ref())
|
||||
inspected_object->decrement_inspector_count({});
|
||||
}
|
||||
|
||||
public:
|
||||
void send_response(JsonObject const& response)
|
||||
{
|
||||
auto serialized = response.to_deprecated_string();
|
||||
auto bytes_to_send = serialized.bytes();
|
||||
u32 length = bytes_to_send.size();
|
||||
// FIXME: Propagate errors
|
||||
MUST(m_socket->write_value(length));
|
||||
while (!bytes_to_send.is_empty()) {
|
||||
size_t bytes_sent = MUST(m_socket->write_some(bytes_to_send));
|
||||
bytes_to_send = bytes_to_send.slice(bytes_sent);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_request(JsonObject const& request)
|
||||
{
|
||||
auto type = request.get_deprecated_string("type"sv);
|
||||
|
||||
if (!type.has_value()) {
|
||||
dbgln("RPC client sent request without type field");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "Identify") {
|
||||
JsonObject response;
|
||||
response.set("type", type.value());
|
||||
response.set("pid", getpid());
|
||||
#ifdef AK_OS_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.value());
|
||||
JsonArray objects;
|
||||
for (auto& object : Object::all_objects()) {
|
||||
JsonObject json_object;
|
||||
object.save_to(json_object);
|
||||
objects.must_append(move(json_object));
|
||||
}
|
||||
response.set("objects", move(objects));
|
||||
send_response(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "SetInspectedObject") {
|
||||
auto address = request.get_addr("address"sv);
|
||||
for (auto& object : Object::all_objects()) {
|
||||
if ((FlatPtr)&object == address) {
|
||||
if (auto inspected_object = m_inspected_object.strong_ref())
|
||||
inspected_object->decrement_inspector_count({});
|
||||
m_inspected_object = object;
|
||||
object.increment_inspector_count({});
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "SetProperty") {
|
||||
auto address = request.get_addr("address"sv);
|
||||
for (auto& object : Object::all_objects()) {
|
||||
if ((FlatPtr)&object == address) {
|
||||
bool success = object.set_property(request.get_deprecated_string("name"sv).value(), request.get("value"sv).value());
|
||||
JsonObject response;
|
||||
response.set("type", "SetProperty");
|
||||
response.set("success", success);
|
||||
send_response(response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NonnullOwnPtr<LocalSocket> m_socket;
|
||||
WeakPtr<Object> m_inspected_object;
|
||||
};
|
||||
|
||||
EventLoop::EventLoop([[maybe_unused]] MakeInspectable make_inspectable)
|
||||
EventLoop::EventLoop()
|
||||
: m_wake_pipe_fds(&s_wake_pipe_fds)
|
||||
, m_private(make<Private>())
|
||||
{
|
||||
@ -325,19 +182,6 @@ EventLoop::EventLoop([[maybe_unused]] MakeInspectable make_inspectable)
|
||||
if (s_event_loop_stack->is_empty()) {
|
||||
s_pid = getpid();
|
||||
s_event_loop_stack->append(*this);
|
||||
|
||||
#ifdef AK_OS_SERENITY
|
||||
if (getuid() != 0) {
|
||||
if (getenv("MAKE_INSPECTABLE") == "1"sv)
|
||||
make_inspectable = Core::EventLoop::MakeInspectable::Yes;
|
||||
|
||||
if (make_inspectable == MakeInspectable::Yes
|
||||
&& !s_inspector_server_connection.with_locked([](auto inspector_server_connection) { return inspector_server_connection; })) {
|
||||
if (!connect_to_inspector_server())
|
||||
dbgln("Core::EventLoop: Failed to connect to InspectorServer");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
initialize_wake_pipes();
|
||||
@ -351,29 +195,6 @@ EventLoop::~EventLoop()
|
||||
s_event_loop_stack->take_last();
|
||||
}
|
||||
|
||||
bool connect_to_inspector_server()
|
||||
{
|
||||
#ifdef AK_OS_SERENITY
|
||||
auto maybe_path = SessionManagement::parse_path_with_sid("/tmp/session/%sid/portal/inspectables"sv);
|
||||
if (maybe_path.is_error()) {
|
||||
dbgln("connect_to_inspector_server: {}", maybe_path.error());
|
||||
return false;
|
||||
}
|
||||
auto inspector_server_path = maybe_path.value();
|
||||
auto maybe_socket = LocalSocket::connect(inspector_server_path, Socket::PreventSIGPIPE::Yes);
|
||||
if (maybe_socket.is_error()) {
|
||||
dbgln("connect_to_inspector_server: Failed to connect: {}", maybe_socket.error());
|
||||
return false;
|
||||
}
|
||||
s_inspector_server_connection.with_locked([&](auto& inspector_server_connection) {
|
||||
inspector_server_connection = InspectorServerConnection::construct(maybe_socket.release_value());
|
||||
});
|
||||
return true;
|
||||
#else
|
||||
VERIFY_NOT_REACHED();
|
||||
#endif
|
||||
}
|
||||
|
||||
#define VERIFY_EVENT_LOOP_INITIALIZED() \
|
||||
do { \
|
||||
if (!s_event_loop_stack) { \
|
||||
|
@ -36,23 +36,16 @@ namespace Core {
|
||||
// - Fork events, because the child process event loop needs to clear its events and handlers.
|
||||
// - Quit events, i.e. the event loop should exit.
|
||||
// Any event that the event loop needs to wait on or needs to repeatedly handle is stored in a handle, e.g. s_timers.
|
||||
//
|
||||
// EventLoop has one final responsibility: Handling the InspectorServer connection and processing requests to the Object hierarchy.
|
||||
class EventLoop {
|
||||
friend struct EventLoopPusher;
|
||||
|
||||
public:
|
||||
enum class MakeInspectable {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
enum class WaitMode {
|
||||
WaitForEvents,
|
||||
PollForEvents,
|
||||
};
|
||||
|
||||
explicit EventLoop(MakeInspectable = MakeInspectable::No);
|
||||
EventLoop();
|
||||
~EventLoop();
|
||||
|
||||
static void initialize_wake_pipes();
|
||||
|
@ -70,11 +70,11 @@ Application* Application::the()
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
Application::Application(int argc, char** argv, Core::EventLoop::MakeInspectable make_inspectable)
|
||||
Application::Application(int argc, char** argv)
|
||||
{
|
||||
VERIFY(!*s_the);
|
||||
*s_the = *this;
|
||||
m_event_loop = make<Core::EventLoop>(make_inspectable);
|
||||
m_event_loop = make<Core::EventLoop>();
|
||||
ConnectionToWindowServer::the();
|
||||
Clipboard::initialize({});
|
||||
if (argc > 0)
|
||||
|
@ -97,9 +97,9 @@ public:
|
||||
void register_recent_file_actions(Badge<GUI::Menu>, Vector<NonnullRefPtr<GUI::Action>>);
|
||||
|
||||
private:
|
||||
Application(int argc, char** argv, Core::EventLoop::MakeInspectable = Core::EventLoop::MakeInspectable::No);
|
||||
Application(Main::Arguments const& arguments, Core::EventLoop::MakeInspectable inspectable = Core::EventLoop::MakeInspectable::No)
|
||||
: Application(arguments.argc, arguments.argv, inspectable)
|
||||
Application(int argc, char** argv);
|
||||
Application(Main::Arguments const& arguments)
|
||||
: Application(arguments.argc, arguments.argv)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ add_subdirectory(ConfigServer)
|
||||
add_subdirectory(EchoServer)
|
||||
add_subdirectory(FileOperation)
|
||||
add_subdirectory(ImageDecoder)
|
||||
add_subdirectory(InspectorServer)
|
||||
add_subdirectory(LookupServer)
|
||||
add_subdirectory(RequestServer)
|
||||
add_subdirectory(WebServer)
|
||||
|
@ -1,22 +0,0 @@
|
||||
serenity_component(
|
||||
InspectorServer
|
||||
REQUIRED
|
||||
TARGETS InspectorServer
|
||||
)
|
||||
|
||||
compile_ipc(InspectorServer.ipc InspectorServerEndpoint.h)
|
||||
compile_ipc(InspectorClient.ipc InspectorClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
ConnectionFromClient.cpp
|
||||
main.cpp
|
||||
InspectableProcess.cpp
|
||||
)
|
||||
|
||||
set(GENERATED_SOURCES
|
||||
InspectorServerEndpoint.h
|
||||
InspectorClientEndpoint.h
|
||||
)
|
||||
|
||||
serenity_bin(InspectorServer)
|
||||
target_link_libraries(InspectorServer PRIVATE LibCore LibIPC LibMain)
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "InspectableProcess.h"
|
||||
#include <AK/JsonObject.h>
|
||||
#include <InspectorServer/ConnectionFromClient.h>
|
||||
|
||||
namespace InspectorServer {
|
||||
|
||||
static HashMap<int, RefPtr<ConnectionFromClient>> s_connections;
|
||||
|
||||
ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> socket, int client_id)
|
||||
: IPC::ConnectionFromClient<InspectorClientEndpoint, InspectorServerEndpoint>(*this, move(socket), client_id)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
}
|
||||
|
||||
Messages::InspectorServer::GetAllObjectsResponse ConnectionFromClient::get_all_objects(pid_t pid)
|
||||
{
|
||||
auto process = InspectableProcess::from_pid(pid);
|
||||
if (!process)
|
||||
return DeprecatedString {};
|
||||
|
||||
JsonObject request;
|
||||
request.set("type", "GetAllObjects");
|
||||
process->send_request(request);
|
||||
auto response = process->wait_for_response();
|
||||
return response;
|
||||
}
|
||||
|
||||
Messages::InspectorServer::SetInspectedObjectResponse ConnectionFromClient::set_inspected_object(pid_t pid, u64 object_id)
|
||||
{
|
||||
auto process = InspectableProcess::from_pid(pid);
|
||||
if (!process)
|
||||
return false;
|
||||
|
||||
JsonObject request;
|
||||
request.set("type", "SetInspectedObject");
|
||||
request.set("address", object_id);
|
||||
process->send_request(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
Messages::InspectorServer::SetObjectPropertyResponse ConnectionFromClient::set_object_property(pid_t pid, u64 object_id, DeprecatedString const& name, DeprecatedString const& value)
|
||||
{
|
||||
auto process = InspectableProcess::from_pid(pid);
|
||||
if (!process)
|
||||
return false;
|
||||
|
||||
JsonObject request;
|
||||
request.set("type", "SetProperty");
|
||||
request.set("address", object_id);
|
||||
request.set("name", name);
|
||||
request.set("value", value);
|
||||
process->send_request(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
Messages::InspectorServer::IdentifyResponse ConnectionFromClient::identify(pid_t pid)
|
||||
{
|
||||
auto process = InspectableProcess::from_pid(pid);
|
||||
if (!process)
|
||||
return DeprecatedString {};
|
||||
|
||||
JsonObject request;
|
||||
request.set("type", "Identify");
|
||||
process->send_request(request);
|
||||
auto response = process->wait_for_response();
|
||||
return response;
|
||||
}
|
||||
|
||||
Messages::InspectorServer::IsInspectableResponse ConnectionFromClient::is_inspectable(pid_t pid)
|
||||
{
|
||||
auto process = InspectableProcess::from_pid(pid);
|
||||
if (!process)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <InspectorServer/InspectorClientEndpoint.h>
|
||||
#include <InspectorServer/InspectorServerEndpoint.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
|
||||
namespace InspectorServer {
|
||||
|
||||
class ConnectionFromClient final
|
||||
: public IPC::ConnectionFromClient<InspectorClientEndpoint, InspectorServerEndpoint> {
|
||||
C_OBJECT(ConnectionFromClient);
|
||||
|
||||
public:
|
||||
~ConnectionFromClient() override = default;
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
private:
|
||||
explicit ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>, int client_id);
|
||||
|
||||
virtual Messages::InspectorServer::GetAllObjectsResponse get_all_objects(pid_t) override;
|
||||
virtual Messages::InspectorServer::SetInspectedObjectResponse set_inspected_object(pid_t, u64 object_id) override;
|
||||
virtual Messages::InspectorServer::SetObjectPropertyResponse set_object_property(pid_t, u64 object_id, DeprecatedString const& name, DeprecatedString const& value) override;
|
||||
virtual Messages::InspectorServer::IdentifyResponse identify(pid_t) override;
|
||||
virtual Messages::InspectorServer::IsInspectableResponse is_inspectable(pid_t) override;
|
||||
};
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace SymbolServer {
|
||||
|
||||
class ConnectionFromClient;
|
||||
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "InspectableProcess.h"
|
||||
#include <AK/JsonObject.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/Socket.h>
|
||||
|
||||
namespace InspectorServer {
|
||||
|
||||
HashMap<pid_t, NonnullOwnPtr<InspectableProcess>> g_processes;
|
||||
|
||||
InspectableProcess* InspectableProcess::from_pid(pid_t pid)
|
||||
{
|
||||
return g_processes.get(pid).value_or(nullptr);
|
||||
}
|
||||
|
||||
InspectableProcess::InspectableProcess(pid_t pid, NonnullOwnPtr<Core::LocalSocket> socket)
|
||||
: m_pid(pid)
|
||||
, m_socket(move(socket))
|
||||
{
|
||||
// FIXME: Propagate errors
|
||||
MUST(m_socket->set_blocking(true));
|
||||
|
||||
m_socket->on_ready_to_read = [this] {
|
||||
[[maybe_unused]] auto c = m_socket->read_value<char>().release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
if (m_socket->is_eof()) {
|
||||
Core::deferred_invoke([pid = this->m_pid] { g_processes.remove(pid); });
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
DeprecatedString InspectableProcess::wait_for_response()
|
||||
{
|
||||
if (m_socket->is_eof()) {
|
||||
dbgln("InspectableProcess disconnected: PID {}", m_pid);
|
||||
m_socket->close();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto length = m_socket->read_value<u32>().release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
auto data_buffer = ByteBuffer::create_uninitialized(length).release_value_but_fixme_should_propagate_errors();
|
||||
auto remaining_data_buffer = data_buffer.bytes();
|
||||
|
||||
while (!remaining_data_buffer.is_empty()) {
|
||||
auto maybe_bytes_read = m_socket->read_some(remaining_data_buffer);
|
||||
if (maybe_bytes_read.is_error()) {
|
||||
dbgln("InspectableProcess::wait_for_response: Failed to read data: {}", maybe_bytes_read.error());
|
||||
break;
|
||||
}
|
||||
|
||||
auto bytes_read = maybe_bytes_read.release_value();
|
||||
if (bytes_read.is_empty())
|
||||
break;
|
||||
|
||||
remaining_data_buffer = remaining_data_buffer.slice(bytes_read.size());
|
||||
}
|
||||
|
||||
VERIFY(data_buffer.size() == length);
|
||||
dbgln("Got data size {} and read that many bytes", length);
|
||||
|
||||
return DeprecatedString::copy(data_buffer);
|
||||
}
|
||||
|
||||
void InspectableProcess::send_request(JsonObject const& request)
|
||||
{
|
||||
auto serialized = request.to_deprecated_string();
|
||||
u32 length = serialized.length();
|
||||
|
||||
// FIXME: Propagate errors
|
||||
MUST(m_socket->write_value(length));
|
||||
MUST(m_socket->write_until_depleted(serialized.bytes()));
|
||||
}
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCore/Socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace InspectorServer {
|
||||
|
||||
class InspectableProcess {
|
||||
public:
|
||||
InspectableProcess(pid_t, NonnullOwnPtr<Core::LocalSocket>);
|
||||
~InspectableProcess() = default;
|
||||
|
||||
void send_request(JsonObject const& request);
|
||||
DeprecatedString wait_for_response();
|
||||
|
||||
static InspectableProcess* from_pid(pid_t);
|
||||
|
||||
private:
|
||||
pid_t m_pid { 0 };
|
||||
NonnullOwnPtr<Core::LocalSocket> m_socket;
|
||||
};
|
||||
|
||||
extern HashMap<pid_t, NonnullOwnPtr<InspectorServer::InspectableProcess>> g_processes;
|
||||
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
endpoint InspectorClient
|
||||
{
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
endpoint InspectorServer
|
||||
{
|
||||
get_all_objects(i32 pid) => (DeprecatedString json)
|
||||
set_inspected_object(i32 pid, u64 object_id) => (bool success)
|
||||
set_object_property(i32 pid, u64 object_id, DeprecatedString name, DeprecatedString value) => (bool success)
|
||||
identify(i32 pid) => (DeprecatedString json)
|
||||
is_inspectable(i32 pid) => (bool inspectable)
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "InspectableProcess.h"
|
||||
#include <InspectorServer/ConnectionFromClient.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
#include <LibIPC/MultiServer.h>
|
||||
#include <LibMain/Main.h>
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments)
|
||||
{
|
||||
Core::EventLoop event_loop;
|
||||
|
||||
TRY(Core::System::pledge("stdio unix accept rpath"));
|
||||
|
||||
auto server = TRY(IPC::MultiServer<InspectorServer::ConnectionFromClient>::try_create("/tmp/session/%sid/portal/inspector"));
|
||||
|
||||
auto inspectables_server = TRY(Core::LocalServer::try_create());
|
||||
TRY(inspectables_server->take_over_from_system_server("/tmp/session/%sid/portal/inspectables"));
|
||||
|
||||
inspectables_server->on_accept = [&](auto client_socket) {
|
||||
auto pid = client_socket->peer_pid().release_value_but_fixme_should_propagate_errors();
|
||||
InspectorServer::g_processes.set(pid, make<InspectorServer::InspectableProcess>(pid, move(client_socket)));
|
||||
};
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
Loading…
Reference in New Issue
Block a user