mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 01:37:34 +00:00
SpaceAnalyzer: Make TreeMapWidget responsible for filesystem analysis
Also, the Tree object was only ever used by the TreeMapWidget, so its creation can happen inside `analyze()`, fixing the memory leak issue. Plus, it doesn't have to be RefCounted.
This commit is contained in:
parent
98e9ee07e3
commit
1ec59cc52a
4 changed files with 82 additions and 93 deletions
|
@ -9,7 +9,6 @@
|
||||||
#include <AK/DeprecatedString.h>
|
#include <AK/DeprecatedString.h>
|
||||||
#include <AK/Forward.h>
|
#include <AK/Forward.h>
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <AK/RefCounted.h>
|
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
|
||||||
struct MountInfo {
|
struct MountInfo {
|
||||||
|
@ -44,16 +43,21 @@ private:
|
||||||
OwnPtr<Vector<TreeNode>> m_children;
|
OwnPtr<Vector<TreeNode>> m_children;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Tree : public RefCounted<Tree> {
|
class Tree {
|
||||||
public:
|
public:
|
||||||
Tree(DeprecatedString root_name)
|
static ErrorOr<NonnullOwnPtr<Tree>> create(DeprecatedString root_name)
|
||||||
: m_root(move(root_name)) {};
|
{
|
||||||
|
return adopt_nonnull_own_or_enomem(new (nothrow) Tree(move(root_name)));
|
||||||
|
}
|
||||||
~Tree() {};
|
~Tree() {};
|
||||||
|
|
||||||
TreeNode& root()
|
TreeNode& root()
|
||||||
{
|
{
|
||||||
return m_root;
|
return m_root;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Tree(DeprecatedString root_name)
|
||||||
|
: m_root(move(root_name)) {};
|
||||||
TreeNode m_root;
|
TreeNode m_root;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2022, the SerenityOS developers.
|
* Copyright (c) 2021-2022, the SerenityOS developers.
|
||||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "TreeMapWidget.h"
|
#include "TreeMapWidget.h"
|
||||||
|
#include "ProgressWindow.h"
|
||||||
#include "Tree.h"
|
#include "Tree.h"
|
||||||
#include <AK/Array.h>
|
#include <AK/Array.h>
|
||||||
#include <AK/DeprecatedString.h>
|
#include <AK/DeprecatedString.h>
|
||||||
#include <AK/NumberFormat.h>
|
#include <AK/NumberFormat.h>
|
||||||
#include <LibGUI/ConnectionToWindowServer.h>
|
#include <LibGUI/ConnectionToWindowServer.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
|
#include <LibGUI/Statusbar.h>
|
||||||
#include <LibGfx/Font/Font.h>
|
#include <LibGfx/Font/Font.h>
|
||||||
#include <WindowServer/WindowManager.h>
|
#include <WindowServer/WindowManager.h>
|
||||||
|
|
||||||
|
@ -373,15 +375,79 @@ void TreeMapWidget::recalculate_path_for_new_tree()
|
||||||
m_viewpoint = new_path_length - 1;
|
m_viewpoint = new_path_length - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreeMapWidget::set_tree(RefPtr<Tree> tree)
|
static ErrorOr<void> fill_mounts(Vector<MountInfo>& output)
|
||||||
{
|
{
|
||||||
m_tree = tree;
|
// Output info about currently mounted filesystems.
|
||||||
|
auto file = TRY(Core::Stream::File::open("/sys/kernel/df"sv, Core::Stream::OpenMode::Read));
|
||||||
|
|
||||||
|
auto content = TRY(file->read_until_eof());
|
||||||
|
auto json = TRY(JsonValue::from_string(content));
|
||||||
|
|
||||||
|
TRY(json.as_array().try_for_each([&output](JsonValue const& value) -> ErrorOr<void> {
|
||||||
|
auto& filesystem_object = value.as_object();
|
||||||
|
MountInfo mount_info;
|
||||||
|
mount_info.mount_point = filesystem_object.get_deprecated_string("mount_point"sv).value_or({});
|
||||||
|
mount_info.source = filesystem_object.get_deprecated_string("source"sv).value_or("none");
|
||||||
|
TRY(output.try_append(mount_info));
|
||||||
|
return {};
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> TreeMapWidget::analyze(GUI::Statusbar& statusbar)
|
||||||
|
{
|
||||||
|
statusbar.set_text("");
|
||||||
|
auto progress_window = TRY(ProgressWindow::try_create("Space Analyzer"sv));
|
||||||
|
progress_window->show();
|
||||||
|
|
||||||
|
// Build an in-memory tree mirroring the filesystem and for each node
|
||||||
|
// calculate the sum of the file size for all its descendants.
|
||||||
|
auto tree = TRY(Tree::create(""));
|
||||||
|
Vector<MountInfo> mounts;
|
||||||
|
TRY(fill_mounts(mounts));
|
||||||
|
auto errors = tree->root().populate_filesize_tree(mounts, [&](size_t processed_file_count) {
|
||||||
|
progress_window->update_progress_label(processed_file_count);
|
||||||
|
});
|
||||||
|
|
||||||
|
progress_window->close();
|
||||||
|
|
||||||
|
// Display an error summary in the statusbar.
|
||||||
|
if (!errors.is_empty()) {
|
||||||
|
StringBuilder builder;
|
||||||
|
bool first = true;
|
||||||
|
builder.append("Some directories were not analyzed: "sv);
|
||||||
|
for (auto& key : errors.keys()) {
|
||||||
|
if (!first) {
|
||||||
|
builder.append(", "sv);
|
||||||
|
}
|
||||||
|
auto const* error = strerror(key);
|
||||||
|
builder.append({ error, strlen(error) });
|
||||||
|
builder.append(" ("sv);
|
||||||
|
int value = errors.get(key).value();
|
||||||
|
builder.append(DeprecatedString::number(value));
|
||||||
|
if (value == 1) {
|
||||||
|
builder.append(" time"sv);
|
||||||
|
} else {
|
||||||
|
builder.append(" times"sv);
|
||||||
|
}
|
||||||
|
builder.append(')');
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
statusbar.set_text(builder.to_deprecated_string());
|
||||||
|
} else {
|
||||||
|
statusbar.set_text("No errors");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_tree = move(tree);
|
||||||
recalculate_path_for_new_tree();
|
recalculate_path_for_new_tree();
|
||||||
|
|
||||||
if (on_path_change) {
|
if (on_path_change) {
|
||||||
on_path_change();
|
on_path_change();
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreeMapWidget::set_viewpoint(size_t viewpoint)
|
void TreeMapWidget::set_viewpoint(size_t viewpoint)
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
TreeNode const* path_node(size_t n) const;
|
TreeNode const* path_node(size_t n) const;
|
||||||
size_t viewpoint() const;
|
size_t viewpoint() const;
|
||||||
void set_viewpoint(size_t);
|
void set_viewpoint(size_t);
|
||||||
void set_tree(RefPtr<Tree> tree);
|
ErrorOr<void> analyze(GUI::Statusbar&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TreeMapWidget() = default;
|
TreeMapWidget() = default;
|
||||||
|
@ -53,7 +53,7 @@ private:
|
||||||
Vector<DeprecatedString> path_to_position(Gfx::IntPoint);
|
Vector<DeprecatedString> path_to_position(Gfx::IntPoint);
|
||||||
void recalculate_path_for_new_tree();
|
void recalculate_path_for_new_tree();
|
||||||
|
|
||||||
RefPtr<Tree> m_tree;
|
OwnPtr<Tree> m_tree;
|
||||||
Vector<DeprecatedString> m_path;
|
Vector<DeprecatedString> m_path;
|
||||||
size_t m_viewpoint { 0 };
|
size_t m_viewpoint { 0 };
|
||||||
void const* m_selected_node_cache;
|
void const* m_selected_node_cache;
|
||||||
|
|
|
@ -5,20 +5,13 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ProgressWindow.h"
|
|
||||||
#include "Tree.h"
|
#include "Tree.h"
|
||||||
#include "TreeMapWidget.h"
|
#include "TreeMapWidget.h"
|
||||||
#include <AK/Error.h>
|
|
||||||
#include <AK/LexicalPath.h>
|
#include <AK/LexicalPath.h>
|
||||||
#include <AK/Queue.h>
|
|
||||||
#include <AK/QuickSort.h>
|
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/StringView.h>
|
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
#include <Applications/SpaceAnalyzer/SpaceAnalyzerGML.h>
|
#include <Applications/SpaceAnalyzer/SpaceAnalyzerGML.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
#include <LibCore/IODevice.h>
|
|
||||||
#include <LibCore/Stream.h>
|
|
||||||
#include <LibDesktop/Launcher.h>
|
#include <LibDesktop/Launcher.h>
|
||||||
#include <LibGUI/Application.h>
|
#include <LibGUI/Application.h>
|
||||||
#include <LibGUI/BoxLayout.h>
|
#include <LibGUI/BoxLayout.h>
|
||||||
|
@ -26,7 +19,6 @@
|
||||||
#include <LibGUI/Clipboard.h>
|
#include <LibGUI/Clipboard.h>
|
||||||
#include <LibGUI/FileIconProvider.h>
|
#include <LibGUI/FileIconProvider.h>
|
||||||
#include <LibGUI/Icon.h>
|
#include <LibGUI/Icon.h>
|
||||||
#include <LibGUI/Label.h>
|
|
||||||
#include <LibGUI/Menu.h>
|
#include <LibGUI/Menu.h>
|
||||||
#include <LibGUI/Menubar.h>
|
#include <LibGUI/Menubar.h>
|
||||||
#include <LibGUI/MessageBox.h>
|
#include <LibGUI/MessageBox.h>
|
||||||
|
@ -36,73 +28,6 @@
|
||||||
|
|
||||||
static constexpr auto APP_NAME = "Space Analyzer"sv;
|
static constexpr auto APP_NAME = "Space Analyzer"sv;
|
||||||
|
|
||||||
static ErrorOr<void> fill_mounts(Vector<MountInfo>& output)
|
|
||||||
{
|
|
||||||
// Output info about currently mounted filesystems.
|
|
||||||
auto file = TRY(Core::Stream::File::open("/sys/kernel/df"sv, Core::Stream::OpenMode::Read));
|
|
||||||
|
|
||||||
auto content = TRY(file->read_until_eof());
|
|
||||||
auto json = TRY(JsonValue::from_string(content));
|
|
||||||
|
|
||||||
TRY(json.as_array().try_for_each([&output](JsonValue const& value) -> ErrorOr<void> {
|
|
||||||
auto& filesystem_object = value.as_object();
|
|
||||||
MountInfo mount_info;
|
|
||||||
mount_info.mount_point = filesystem_object.get_deprecated_string("mount_point"sv).value_or({});
|
|
||||||
mount_info.source = filesystem_object.get_deprecated_string("source"sv).value_or("none");
|
|
||||||
TRY(output.try_append(mount_info));
|
|
||||||
return {};
|
|
||||||
}));
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static ErrorOr<void> analyze(RefPtr<Tree> tree, SpaceAnalyzer::TreeMapWidget& treemapwidget, GUI::Statusbar& statusbar)
|
|
||||||
{
|
|
||||||
statusbar.set_text("");
|
|
||||||
auto progress_window = TRY(ProgressWindow::try_create(APP_NAME));
|
|
||||||
progress_window->show();
|
|
||||||
|
|
||||||
// Build an in-memory tree mirroring the filesystem and for each node
|
|
||||||
// calculate the sum of the file size for all its descendants.
|
|
||||||
Vector<MountInfo> mounts;
|
|
||||||
TRY(fill_mounts(mounts));
|
|
||||||
auto errors = tree->root().populate_filesize_tree(mounts, [&](size_t processed_file_count) {
|
|
||||||
progress_window->update_progress_label(processed_file_count);
|
|
||||||
});
|
|
||||||
|
|
||||||
progress_window->close();
|
|
||||||
|
|
||||||
// Display an error summary in the statusbar.
|
|
||||||
if (!errors.is_empty()) {
|
|
||||||
StringBuilder builder;
|
|
||||||
bool first = true;
|
|
||||||
builder.append("Some directories were not analyzed: "sv);
|
|
||||||
for (auto& key : errors.keys()) {
|
|
||||||
if (!first) {
|
|
||||||
builder.append(", "sv);
|
|
||||||
}
|
|
||||||
auto const* error = strerror(key);
|
|
||||||
builder.append({ error, strlen(error) });
|
|
||||||
builder.append(" ("sv);
|
|
||||||
int value = errors.get(key).value();
|
|
||||||
builder.append(DeprecatedString::number(value));
|
|
||||||
if (value == 1) {
|
|
||||||
builder.append(" time"sv);
|
|
||||||
} else {
|
|
||||||
builder.append(" times"sv);
|
|
||||||
}
|
|
||||||
builder.append(')');
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
statusbar.set_text(builder.to_deprecated_string());
|
|
||||||
} else {
|
|
||||||
statusbar.set_text("No errors");
|
|
||||||
}
|
|
||||||
treemapwidget.set_tree(tree);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeprecatedString get_absolute_path_to_selected_node(SpaceAnalyzer::TreeMapWidget const& treemapwidget, bool include_last_node = true)
|
static DeprecatedString get_absolute_path_to_selected_node(SpaceAnalyzer::TreeMapWidget const& treemapwidget, bool include_last_node = true)
|
||||||
{
|
{
|
||||||
StringBuilder path_builder;
|
StringBuilder path_builder;
|
||||||
|
@ -120,8 +45,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
auto app = TRY(GUI::Application::try_create(arguments));
|
auto app = TRY(GUI::Application::try_create(arguments));
|
||||||
|
|
||||||
RefPtr<Tree> tree = adopt_ref(*new Tree(""));
|
|
||||||
|
|
||||||
// Configure application window.
|
// Configure application window.
|
||||||
auto app_icon = GUI::Icon::default_icon("app-space-analyzer"sv);
|
auto app_icon = GUI::Icon::default_icon("app-space-analyzer"sv);
|
||||||
auto window = TRY(GUI::Window::try_create());
|
auto window = TRY(GUI::Window::try_create());
|
||||||
|
@ -141,9 +64,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
auto file_menu = TRY(window->try_add_menu("&File"));
|
auto file_menu = TRY(window->try_add_menu("&File"));
|
||||||
TRY(file_menu->try_add_action(GUI::Action::create("&Analyze", [&](auto&) {
|
TRY(file_menu->try_add_action(GUI::Action::create("&Analyze", [&](auto&) {
|
||||||
// FIXME: Just modify the tree in memory instead of traversing the entire file system
|
// FIXME: Just modify the tree in memory instead of traversing the entire file system
|
||||||
// FIXME: Dispose of the old tree
|
if (auto result = treemapwidget.analyze(statusbar); result.is_error()) {
|
||||||
auto new_tree = adopt_ref(*new Tree(""));
|
|
||||||
if (auto result = analyze(new_tree, treemapwidget, statusbar); result.is_error()) {
|
|
||||||
GUI::MessageBox::show_error(window, DeprecatedString::formatted("{}", result.error()));
|
GUI::MessageBox::show_error(window, DeprecatedString::formatted("{}", result.error()));
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
@ -197,9 +118,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Dispose of the old tree
|
if (auto result = treemapwidget.analyze(statusbar); result.is_error()) {
|
||||||
auto new_tree = adopt_ref(*new Tree(""));
|
|
||||||
if (auto result = analyze(new_tree, treemapwidget, statusbar); result.is_error()) {
|
|
||||||
GUI::MessageBox::show_error(window, DeprecatedString::formatted("{}", result.error()));
|
GUI::MessageBox::show_error(window, DeprecatedString::formatted("{}", result.error()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -257,7 +176,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
};
|
};
|
||||||
|
|
||||||
// At startup automatically do an analysis of root.
|
// At startup automatically do an analysis of root.
|
||||||
TRY(analyze(tree, treemapwidget, statusbar));
|
TRY(treemapwidget.analyze(statusbar));
|
||||||
|
|
||||||
window->show();
|
window->show();
|
||||||
return app->exec();
|
return app->exec();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue