Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
profile: Host debug daemon for opening in Speedscope
Browse files Browse the repository at this point in the history
By running `scripts/debugd.py` on the host machine with speedscope installed, one can now pass the `-r` flag to `profile` in order to send the output directly to the host via networking and open it in speedscope.
  • Loading branch information
byteduck committed Jul 20, 2024
1 parent d080b91 commit 9defc53
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 34 deletions.
108 changes: 74 additions & 34 deletions programs/coreutils/profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@
#include <sys/wait.h>
#include <libduck/FormatStream.h>
#include <libduck/Args.h>
#include <libduck/Socket.h>
#include <ctime>

constexpr int debugd_port = 59336;
constexpr const char* debugd_start = "DEBUGD\nPROFILE\n";

using namespace Debug;
using Duck::OutputStream;

int pid;
int interval = 10;
int duration = 5000;
bool remote = false;
std::string filename;

struct Thread {
Expand All @@ -23,6 +28,43 @@ struct Thread {
std::vector<std::vector<size_t>> stacks;
};

Duck::Result output_profile(
Duck::OutputStream& stream,
std::map<size_t, AddressInfo>& symbols,
std::vector<Duck::Ptr<Thread>>& threads) {
for (auto& thread : threads){
for (auto& stk : thread->stacks) {
stream << "thread " << thread->tid << ";";
for(size_t i = stk.size(); i > 0; i--) {
auto pos = stk[i - 1];
auto info = symbols.find(pos);
if (info == symbols.end()) {
auto info_res = thread->debugger.info_at(pos);
if(info_res.is_error())
symbols[pos] = {"???", pos, nullptr};
else
symbols[pos] = info_res.value();
info = symbols.find(pos);
}

auto& symbol = info->second;
if (!symbol.object)
stream % "?? @ {#x}" % symbol.symbol_offset;
else if(symbol.symbol_name == "__syscall_trap__")
stream << "<kernel>";
else
stream % "{} @ {}" % symbol.symbol_name % symbol.object->name;

if (i != 1)
stream << ";";
}
stream << " 1\n";
}
}

return Duck::Result::SUCCESS;
}

Duck::Result profile() {
auto proc = TRY(Sys::Process::get(pid));

Expand Down Expand Up @@ -58,44 +100,41 @@ Duck::Result profile() {
Duck::println("Done! Symbolicating and dumping...");
std::map<size_t, AddressInfo> symbols;

if (filename.empty())
filename = "profile-" + proc.name() + "-" + std::to_string(std::time(nullptr)) + ".txt";
auto out = TRY(Duck::File::open(filename, "w"));
Duck::FileOutputStream stream {out};

for (auto& thread : threads) {
for (auto& stk : thread->stacks) {
stream << "thread " << thread->tid << ";";
for(size_t i = stk.size(); i > 0; i--) {
auto pos = stk[i - 1];
auto info = symbols.find(pos);
if (info == symbols.end()) {
auto info_res = thread->debugger.info_at(pos);
if(info_res.is_error())
symbols[pos] = {"???", pos, nullptr};
else
symbols[pos] = info_res.value();
info = symbols.find(pos);
}

auto& symbol = info->second;
if (!symbol.object)
stream % "?? @ {#x}" % symbol.symbol_offset;
else if(symbol.symbol_name == "__syscall_trap__")
stream << "<kernel>";
else
stream % "{} @ {}" % symbol.symbol_name % symbol.object->name;

if (i != 1)
stream << ";";
}
stream << " 1\n";
if (remote) {
// Collect into StringOutputStream
Duck::StringOutputStream stream;
TRYRES(output_profile(stream, symbols, threads));

// Connect to socket
Duck::print("Connecting to debug daemon... ", debugd_port);
fflush(stdout);
auto sock = TRY(Duck::Socket::open(Duck::Socket::Inet, Duck::Socket::Stream, Duck::Socket::TCP));
TRYRES(sock.connect({10, 0, 2, 2}, debugd_port));

// Write to socket
Duck::print("Sending... ");
fflush(stdout);

TRYRES(sock.send(debugd_start, strlen(debugd_start)));
for (size_t i = 0; i < stream.string().length() + 1; i += 1024) {
auto len = std::min((size_t) 1024, stream.string().length() + 1 - i);
TRYRES(sock.send(stream.string().c_str() + i, len));
}

Duck::println("Sent!");
sock.close();
} else {
// Write to file
if (filename.empty())
filename = "profile-" + proc.name() + "-" + std::to_string(std::time(nullptr)) + ".txt";
auto out = TRY(Duck::File::open(filename, "w"));
Duck::FileOutputStream fs {out};
TRYRES(output_profile(fs, symbols, threads));
out.close();
Duck::println("Done! Saved to {}.", filename);
}

out.close();

Duck::println("Done! Saved to {}.", filename);
return Duck::Result::SUCCESS;
}

Expand All @@ -105,6 +144,7 @@ int main(int argc, char** argv) {
args.add_named(interval, "i", "interval", "The interval with which to sample, in ms. (Default: 10)");
args.add_named(duration, "d", "duration", "The duration to sample for, in ms. (Default: 5000)");
args.add_named(filename, "o", "output", "The output file.");
args.add_flag(remote, "r", "remote", "Sends the output to a remote debug server.");
args.parse(argc, argv);

auto res = profile();
Expand Down
40 changes: 40 additions & 0 deletions scripts/debugd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import socketserver
import tempfile
import subprocess

DEBUGD_HEADER = "DEBUGD"
PROFILE_HEADER = f"{DEBUGD_HEADER}\nPROFILE\n"

class DebugDaemonHandler(socketserver.BaseRequestHandler):
def handle(self):
# Receive whole request into a string
outstr = ""
while True:
data = self.request.recv(1024)
outstr += str(data, encoding="utf-8")
if data[-1] == 0:
break

# Check validity
if not outstr.startswith(DEBUGD_HEADER):
print(f"Received invalid header from {self.client_address[0]}")
if not outstr.startswith(PROFILE_HEADER):
print(f"Received invalid request {outstr.splitlines()[1]} from {self.client_address[0]}")

# Write to tmp file and open speedscope
print(f"Received profile from {self.client_address[0]}")
out = tempfile.NamedTemporaryFile(delete=False)
out.write(outstr[len(PROFILE_HEADER):].encode("utf-8"))
out.close()
subprocess.run(["speedscope", out.name])


if __name__ == "__main__":
HOST, PORT = "localhost", 59336

with socketserver.TCPServer((HOST, PORT), DebugDaemonHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
print(f"Running duckOS debugd on {HOST}:{PORT}")
server.serve_forever()

0 comments on commit 9defc53

Please sign in to comment.