summaryrefslogtreecommitdiff
path: root/Userland/DevTools/Profiler/Process.cpp
blob: 2fdcb376ea896b1d07276298b24012ab19d14791 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "Process.h"
#include <LibCore/DeprecatedFile.h>

namespace Profiler {

Thread* Process::find_thread(pid_t tid, EventSerialNumber serial)
{
    auto it = threads.find(tid);
    if (it == threads.end())
        return nullptr;
    for (auto& thread : it->value) {
        if (thread.start_valid < serial && (thread.end_valid == EventSerialNumber {} || thread.end_valid > serial))
            return &thread;
    }
    return nullptr;
}

void Process::handle_thread_create(pid_t tid, EventSerialNumber serial)
{
    auto it = threads.find(tid);
    if (it == threads.end()) {
        threads.set(tid, {});
        it = threads.find(tid);
    }

    auto thread = Thread { tid, serial, {} };
    it->value.append(move(thread));
}

void Process::handle_thread_exit(pid_t tid, EventSerialNumber serial)
{
    auto* thread = find_thread(tid, serial);
    if (!thread)
        return;
    thread->end_valid = serial;
}

HashMap<DeprecatedString, OwnPtr<MappedObject>> g_mapped_object_cache;

static MappedObject* get_or_create_mapped_object(DeprecatedString const& path)
{
    if (auto it = g_mapped_object_cache.find(path); it != g_mapped_object_cache.end())
        return it->value.ptr();

    auto file_or_error = Core::MappedFile::map(path);
    if (file_or_error.is_error()) {
        g_mapped_object_cache.set(path, {});
        return nullptr;
    }
    auto elf = ELF::Image(file_or_error.value()->bytes());
    if (!elf.is_valid()) {
        g_mapped_object_cache.set(path, {});
        return nullptr;
    }
    auto new_mapped_object = adopt_own(*new MappedObject {
        .file = file_or_error.release_value(),
        .elf = elf,
    });
    auto* ptr = new_mapped_object.ptr();
    g_mapped_object_cache.set(path, move(new_mapped_object));
    return ptr;
}

void LibraryMetadata::handle_mmap(FlatPtr base, size_t size, DeprecatedString const& name)
{
    StringView path;
    if (name.contains("Loader.so"sv))
        path = "Loader.so"sv;
    else if (!name.contains(':'))
        return;
    else
        path = name.substring_view(0, name.view().find(':').value());

    // Each loaded object has at least 4 segments associated with it: .rodata, .text, .relro, .data.
    // We only want to create a single LibraryMetadata object for each library, so we need to update the
    // associated base address and size as new regions are discovered.

    // We don't allocate a temporary String object if an entry already exists.
    // This assumes that DeprecatedString::hash and StringView::hash return the same result.
    auto string_view_compare = [&path](auto& entry) { return path == entry.key.view(); };
    if (auto existing_it = m_libraries.find(path.hash(), string_view_compare); existing_it != m_libraries.end()) {
        auto& entry = *existing_it->value;
        entry.base = min(entry.base, base);
        entry.size = max(entry.size + size, base - entry.base + size);
    } else {
        DeprecatedString path_string = path.to_deprecated_string();
        DeprecatedString full_path;
        if (path_string.starts_with('/'))
            full_path = path_string;
        else if (Core::DeprecatedFile::looks_like_shared_library(path_string))
            full_path = DeprecatedString::formatted("/usr/lib/{}", path);
        else
            full_path = path_string;

        auto* mapped_object = get_or_create_mapped_object(full_path);
        if (!mapped_object) {
            full_path = DeprecatedString::formatted("/usr/local/lib/{}", path);
            mapped_object = get_or_create_mapped_object(full_path);
            if (!mapped_object)
                return;
        }
        m_libraries.set(path_string, adopt_own(*new Library { base, size, path_string, mapped_object, {} }));
    }
}

Debug::DebugInfo const& LibraryMetadata::Library::load_debug_info(FlatPtr base_address) const
{
    if (debug_info == nullptr)
        debug_info = make<Debug::DebugInfo>(object->elf, DeprecatedString::empty(), base_address);
    return *debug_info.ptr();
}

DeprecatedString LibraryMetadata::Library::symbolicate(FlatPtr ptr, u32* offset) const
{
    if (!object)
        return DeprecatedString::formatted("?? <{:p}>", ptr);

    return object->elf.symbolicate(ptr - base, offset);
}

LibraryMetadata::Library const* LibraryMetadata::library_containing(FlatPtr ptr) const
{
    for (auto& it : m_libraries) {
        auto& library = *it.value;
        if (ptr >= library.base && ptr < (library.base + library.size))
            return &library;
    }
    return nullptr;
}

}