800 lines
23 KiB
C++
800 lines
23 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file pStatMonitor.cxx
|
|
* @author drose
|
|
* @date 2000-07-09
|
|
*/
|
|
|
|
#include "pStatMonitor.h"
|
|
|
|
#include "datagramInputFile.h"
|
|
#include "datagramOutputFile.h"
|
|
#include "pandaVersion.h"
|
|
#include "pStatCollectorDef.h"
|
|
#include "pStatTimeline.h"
|
|
#include "pStatStripChart.h"
|
|
#include "pStatFlameGraph.h"
|
|
#include "pStatPianoRoll.h"
|
|
|
|
using std::string;
|
|
|
|
static const string session_file_header("pstat\0\n\r", 8);
|
|
static const string layout_file_header("pslyt\0\n\r", 8);
|
|
|
|
static const Filename layout_filename = Filename::binary_filename(
|
|
#ifdef _WIN32
|
|
Filename::expand_from("$USER_APPDATA/Panda3D-" PANDA_ABI_VERSION_STR "/pstats-layout")
|
|
#elif defined(__APPLE__)
|
|
Filename::expand_from("$HOME/Library/Caches/Panda3D-" PANDA_ABI_VERSION_STR "/pstats-layout")
|
|
#else
|
|
Filename::expand_from("$XDG_STATE_HOME/panda3d/pstats-layout")
|
|
#endif
|
|
);
|
|
|
|
/**
|
|
*
|
|
*/
|
|
PStatMonitor::
|
|
PStatMonitor(PStatServer *server) : _server(server) {
|
|
_client_known = false;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
PStatMonitor::
|
|
~PStatMonitor() {
|
|
close();
|
|
}
|
|
|
|
/**
|
|
* Called shortly after startup time with the greeting from the client. This
|
|
* indicates the client's reported hostname and program name.
|
|
*/
|
|
void PStatMonitor::
|
|
hello_from(const string &hostname, const string &progname, int pid) {
|
|
_client_known = true;
|
|
_client_hostname = hostname;
|
|
_client_progname = progname;
|
|
_client_pid = pid;
|
|
got_hello();
|
|
}
|
|
|
|
/**
|
|
* Called shortly after startup time with the greeting from the client. In
|
|
* this case, the client seems to have an incompatible version and will be
|
|
* automatically disconnected; the server should issue a message to that
|
|
* effect.
|
|
*/
|
|
void PStatMonitor::
|
|
bad_version(const string &hostname, const string &progname, int pid,
|
|
int client_major, int client_minor,
|
|
int server_major, int server_minor) {
|
|
_client_known = true;
|
|
_client_hostname = hostname;
|
|
_client_progname = progname;
|
|
_client_pid = 0;
|
|
got_bad_version(client_major, client_minor,
|
|
server_major, server_minor);
|
|
}
|
|
|
|
/**
|
|
* Called by the PStatServer at setup time to set the new data pointer for the
|
|
* first time.
|
|
*/
|
|
void PStatMonitor::
|
|
set_client_data(PStatClientData *client_data) {
|
|
_client_data = client_data;
|
|
initialized();
|
|
}
|
|
|
|
/**
|
|
* Writes the data and the UI state to the given file.
|
|
*/
|
|
bool PStatMonitor::
|
|
write(const Filename &fn) const {
|
|
DatagramOutputFile dof;
|
|
if (!dof.open(fn)) {
|
|
return false;
|
|
}
|
|
if (!dof.write_header(session_file_header)) {
|
|
return false;
|
|
}
|
|
Datagram dg;
|
|
dg.set_stdfloat_double(false);
|
|
dg.add_uint16(1);
|
|
dg.add_uint16(1);
|
|
write_datagram(dg);
|
|
dof.put_datagram(dg);
|
|
dof.close();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Reads the data and the UI state from the given file.
|
|
*/
|
|
bool PStatMonitor::
|
|
read(const Filename &fn) {
|
|
close();
|
|
|
|
DatagramInputFile dif;
|
|
if (!dif.open(fn)) {
|
|
nout << "Failed to open " << fn << " for reading.\n";
|
|
return false;
|
|
}
|
|
string header;
|
|
if (!dif.read_header(header, 8) || header != session_file_header) {
|
|
nout << "Session file contains invalid header.\n";
|
|
return false;
|
|
}
|
|
Datagram dg;
|
|
dg.set_stdfloat_double(false);
|
|
|
|
if (!dif.get_datagram(dg)) {
|
|
nout << "Failed to read datagram from session file.\n";
|
|
return false;
|
|
}
|
|
|
|
DatagramIterator scan(dg);
|
|
int version = scan.get_uint16();
|
|
if (version != 1) {
|
|
nout << "Unsupported session file version " << version << ".\n";
|
|
return false;
|
|
}
|
|
// Room for a minor version number
|
|
scan.get_uint16();
|
|
|
|
read_datagram(scan);
|
|
dif.close();
|
|
|
|
idle();
|
|
|
|
_read_filename = fn;
|
|
_client_data->clear_dirty();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Opens the default set of graphs.
|
|
*/
|
|
void PStatMonitor::
|
|
open_default_graphs() {
|
|
DatagramInputFile dif;
|
|
if (!dif.open(layout_filename)) {
|
|
open_strip_chart(0, 0, false);
|
|
return;
|
|
}
|
|
string header;
|
|
if (!dif.read_header(header, 8) || header != layout_file_header) {
|
|
nout << "Layout file contains invalid header.\n";
|
|
open_strip_chart(0, 0, false);
|
|
return;
|
|
}
|
|
Datagram dg;
|
|
dg.set_stdfloat_double(false);
|
|
|
|
if (!dif.get_datagram(dg)) {
|
|
nout << "Failed to read datagram from layout file.\n";
|
|
open_strip_chart(0, 0, false);
|
|
return;
|
|
}
|
|
|
|
DatagramIterator scan(dg);
|
|
int version = scan.get_uint16();
|
|
if (version != 1) {
|
|
nout << "Unsupported layout file version " << version << ".\n";
|
|
open_strip_chart(0, 0, false);
|
|
return;
|
|
}
|
|
// Room for a minor version number
|
|
scan.get_uint16();
|
|
|
|
size_t num_colors = scan.get_uint32();
|
|
for (size_t i = 0; i < num_colors; ++i) {
|
|
int key = scan.get_int32();
|
|
LRGBColor &color = _colors[key];
|
|
color[0] = scan.get_float32();
|
|
color[1] = scan.get_float32();
|
|
color[2] = scan.get_float32();
|
|
}
|
|
|
|
PStatGraph *graph;
|
|
|
|
size_t num_timelines = scan.get_uint16();
|
|
for (size_t i = 0; i < num_timelines; ++i) {
|
|
int x = scan.get_int32();
|
|
int y = scan.get_int32();
|
|
int width = scan.get_int32();
|
|
int height = scan.get_int32();
|
|
bool minimized = scan.get_bool();
|
|
bool maximized = scan.get_bool();
|
|
graph = open_timeline();
|
|
graph->set_window_state(x, y, width, height, minimized, maximized);
|
|
}
|
|
|
|
size_t num_strip_charts = scan.get_uint16();
|
|
for (size_t i = 0; i < num_strip_charts; ++i) {
|
|
std::string thread_name = scan.get_string();
|
|
std::string collector_name = scan.get_string();
|
|
bool show_level = scan.get_bool();
|
|
int thread_index = _client_data->find_thread(thread_name);
|
|
int collector_index = _client_data->find_collector(collector_name);
|
|
int x = scan.get_int32();
|
|
int y = scan.get_int32();
|
|
int width = scan.get_int32();
|
|
int height = scan.get_int32();
|
|
bool minimized = scan.get_bool();
|
|
bool maximized = scan.get_bool();
|
|
if (thread_index != -1) {
|
|
graph = open_strip_chart(thread_index, collector_index, show_level);
|
|
graph->set_window_state(x, y, width, height, minimized, maximized);
|
|
}
|
|
}
|
|
|
|
size_t num_flame_graphs = scan.get_uint16();
|
|
for (size_t i = 0; i < num_flame_graphs; ++i) {
|
|
std::string thread_name = scan.get_string();
|
|
std::string collector_name = scan.get_string();
|
|
int thread_index = _client_data->find_thread(thread_name);
|
|
int collector_index = collector_name.empty() ? -1 : _client_data->find_collector(collector_name);
|
|
int x = scan.get_int32();
|
|
int y = scan.get_int32();
|
|
int width = scan.get_int32();
|
|
int height = scan.get_int32();
|
|
bool minimized = scan.get_bool();
|
|
bool maximized = scan.get_bool();
|
|
if (thread_index != -1) {
|
|
graph = open_flame_graph(thread_index, collector_index);
|
|
graph->set_window_state(x, y, width, height, minimized, maximized);
|
|
}
|
|
}
|
|
|
|
size_t num_piano_rolls = scan.get_uint16();
|
|
for (size_t i = 0; i < num_piano_rolls; ++i) {
|
|
std::string thread_name = scan.get_string();
|
|
int thread_index = _client_data->find_thread(thread_name);
|
|
int x = scan.get_int32();
|
|
int y = scan.get_int32();
|
|
int width = scan.get_int32();
|
|
int height = scan.get_int32();
|
|
bool minimized = scan.get_bool();
|
|
bool maximized = scan.get_bool();
|
|
if (thread_index != -1) {
|
|
graph = open_piano_roll(thread_index);
|
|
graph->set_window_state(x, y, width, height, minimized, maximized);
|
|
}
|
|
}
|
|
|
|
dif.close();
|
|
if (num_timelines + num_strip_charts + num_flame_graphs + num_piano_rolls == 0) {
|
|
open_strip_chart(0, 0, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves the current graph layout as the default graph layout.
|
|
*/
|
|
bool PStatMonitor::
|
|
save_default_graphs() const {
|
|
layout_filename.make_dir();
|
|
|
|
DatagramOutputFile dof;
|
|
if (!dof.open(layout_filename)) {
|
|
return false;
|
|
}
|
|
if (!dof.write_header(layout_file_header)) {
|
|
return false;
|
|
}
|
|
Datagram dg;
|
|
dg.set_stdfloat_double(false);
|
|
dg.add_uint16(1);
|
|
dg.add_uint16(1);
|
|
|
|
dg.add_uint32((uint32_t)_colors.size());
|
|
for (const auto &item : _colors) {
|
|
dg.add_int32(item.first);
|
|
dg.add_float32(item.second[0]);
|
|
dg.add_float32(item.second[1]);
|
|
dg.add_float32(item.second[2]);
|
|
}
|
|
|
|
dg.add_uint16(_timelines.size());
|
|
for (PStatGraph *graph : _timelines) {
|
|
int x, y, width, height;
|
|
bool minimized, maximized;
|
|
if (graph->get_window_state(x, y, width, height, minimized, maximized)) {
|
|
dg.add_int32(x);
|
|
dg.add_int32(y);
|
|
dg.add_int32(width);
|
|
dg.add_int32(height);
|
|
dg.add_bool(minimized);
|
|
dg.add_bool(maximized);
|
|
}
|
|
}
|
|
|
|
dg.add_uint16(_strip_charts.size());
|
|
for (PStatGraph *graph : _strip_charts) {
|
|
int x, y, width, height;
|
|
bool minimized, maximized;
|
|
if (graph->get_window_state(x, y, width, height, minimized, maximized)) {
|
|
dg.add_string(_client_data->get_thread_name(((PStatStripChart *)graph)->get_thread_index()));
|
|
dg.add_string(_client_data->get_collector_fullname(((PStatStripChart *)graph)->get_collector_index()));
|
|
dg.add_bool(((PStatStripChart *)graph)->get_view().get_show_level());
|
|
dg.add_int32(x);
|
|
dg.add_int32(y);
|
|
dg.add_int32(width);
|
|
dg.add_int32(height);
|
|
dg.add_bool(minimized);
|
|
dg.add_bool(maximized);
|
|
}
|
|
}
|
|
|
|
dg.add_uint16(_flame_graphs.size());
|
|
for (PStatGraph *graph : _flame_graphs) {
|
|
int x, y, width, height;
|
|
bool minimized, maximized;
|
|
if (graph->get_window_state(x, y, width, height, minimized, maximized)) {
|
|
int collector_index = ((PStatFlameGraph *)graph)->get_collector_index();
|
|
dg.add_string(_client_data->get_thread_name(((PStatFlameGraph *)graph)->get_thread_index()));
|
|
dg.add_string(collector_index >= 0 ? _client_data->get_collector_fullname(collector_index) : "");
|
|
dg.add_int32(x);
|
|
dg.add_int32(y);
|
|
dg.add_int32(width);
|
|
dg.add_int32(height);
|
|
dg.add_bool(minimized);
|
|
dg.add_bool(maximized);
|
|
}
|
|
}
|
|
|
|
dg.add_uint16(_piano_rolls.size());
|
|
for (PStatGraph *graph : _piano_rolls) {
|
|
int x, y, width, height;
|
|
bool minimized, maximized;
|
|
if (graph->get_window_state(x, y, width, height, minimized, maximized)) {
|
|
dg.add_string(_client_data->get_thread_name(((PStatPianoRoll *)graph)->get_thread_index()));
|
|
dg.add_int32(x);
|
|
dg.add_int32(y);
|
|
dg.add_int32(width);
|
|
dg.add_int32(height);
|
|
dg.add_bool(minimized);
|
|
dg.add_bool(maximized);
|
|
}
|
|
}
|
|
|
|
// Reserved for future graph type
|
|
dg.add_uint16(0);
|
|
|
|
dof.put_datagram(dg);
|
|
dof.close();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the client is alive and connected, false otherwise.
|
|
*/
|
|
bool PStatMonitor::
|
|
is_alive() const {
|
|
if (_client_data.is_null()) {
|
|
// Not yet, but in a second probably.
|
|
return false;
|
|
}
|
|
return _client_data->is_alive();
|
|
}
|
|
|
|
/**
|
|
* Closes the client connection if it is active.
|
|
*/
|
|
void PStatMonitor::
|
|
close() {
|
|
if (!_client_data.is_null()) {
|
|
_client_data->close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the color associated with the indicated collector. If the
|
|
* collector has no associated color, or is unknown, a new color will be made
|
|
* up on the spot and associated with this collector for the rest of the
|
|
* session.
|
|
*/
|
|
const LRGBColor &PStatMonitor::
|
|
get_collector_color(int collector_index) {
|
|
Colors::iterator ci;
|
|
ci = _colors.find(collector_index);
|
|
if (ci != _colors.end()) {
|
|
return (*ci).second;
|
|
}
|
|
|
|
// Ask the client data about the color.
|
|
if (!_client_data.is_null() &&
|
|
_client_data->has_collector(collector_index)) {
|
|
const PStatCollectorDef &def =
|
|
_client_data->get_collector_def(collector_index);
|
|
|
|
LRGBColor sc(def._suggested_color.r,
|
|
def._suggested_color.g,
|
|
def._suggested_color.b);
|
|
if (sc != LRGBColor::zero()) {
|
|
ci = _colors.insert(Colors::value_type(collector_index, sc)).first;
|
|
return (*ci).second;
|
|
}
|
|
|
|
// Use the fullname of the collector as a hash to seed the random number
|
|
// generator (consulted below), so we get the same color for a given name
|
|
// across sessions.
|
|
string fullname = _client_data->get_collector_fullname(collector_index);
|
|
unsigned int hash = 0;
|
|
for (string::const_iterator ci = fullname.begin(); ci != fullname.end(); ++ci) {
|
|
hash = hash * 37 + (unsigned int)(*ci);
|
|
}
|
|
srand(hash);
|
|
}
|
|
|
|
// We didn't have a color for the collector; make one up.
|
|
LRGBColor random_color;
|
|
random_color[0] = (double)rand() / (double)RAND_MAX;
|
|
random_color[1] = (double)rand() / (double)RAND_MAX;
|
|
random_color[2] = (double)rand() / (double)RAND_MAX;
|
|
|
|
ci = _colors.insert(Colors::value_type(collector_index, random_color)).first;
|
|
return (*ci).second;
|
|
}
|
|
|
|
/**
|
|
* Sets a custom color associated with the given collector.
|
|
*/
|
|
void PStatMonitor::
|
|
set_collector_color(int collector_index, const LRGBColor &color) {
|
|
_colors[collector_index] = color;
|
|
}
|
|
|
|
/**
|
|
* Clears any custom custom color associated with the given collector.
|
|
*/
|
|
void PStatMonitor::
|
|
clear_collector_color(int collector_index) {
|
|
_colors.erase(collector_index);
|
|
}
|
|
|
|
/**
|
|
* Returns a view on the given thread index. If there is no such view already
|
|
* for the indicated thread, this will create one. This view can be used to
|
|
* examine the accumulated data for the given thread.
|
|
*/
|
|
PStatView &PStatMonitor::
|
|
get_view(int thread_index) {
|
|
Views::iterator vi;
|
|
vi = _views.find(thread_index);
|
|
if (vi == _views.end()) {
|
|
vi = _views.insert(Views::value_type(thread_index, PStatView())).first;
|
|
(*vi).second.set_thread_data(_client_data->get_thread_data(thread_index));
|
|
}
|
|
return (*vi).second;
|
|
}
|
|
|
|
/**
|
|
* Returns a view on the level value (as opposed to elapsed time) for the
|
|
* given collector over the given thread. If there is no such view already
|
|
* for the indicated thread, this will create one.
|
|
*/
|
|
PStatView &PStatMonitor::
|
|
get_level_view(int collector_index, int thread_index) {
|
|
LevelViews::iterator lvi;
|
|
lvi = _level_views.find(collector_index);
|
|
if (lvi == _level_views.end()) {
|
|
lvi = _level_views.insert(LevelViews::value_type(collector_index, Views())).first;
|
|
}
|
|
Views &views = (*lvi).second;
|
|
|
|
Views::iterator vi;
|
|
vi = views.find(thread_index);
|
|
if (vi == views.end()) {
|
|
vi = views.insert(Views::value_type(thread_index, PStatView())).first;
|
|
(*vi).second.set_thread_data(_client_data->get_thread_data(thread_index));
|
|
(*vi).second.constrain(collector_index, true);
|
|
}
|
|
return (*vi).second;
|
|
}
|
|
|
|
/**
|
|
* Called after the monitor has been fully set up. At this time, it will have
|
|
* a valid _client_data pointer, and things like is_alive() and close() will
|
|
* be meaningful. However, we may not yet know who we're connected to
|
|
* (is_client_known() may return false), and we may not know anything about
|
|
* the threads or collectors we're about to get data on.
|
|
*/
|
|
void PStatMonitor::
|
|
initialized() {
|
|
}
|
|
|
|
/**
|
|
* Called when the "hello" message has been received from the client. At this
|
|
* time, the client's hostname and program name will be known.
|
|
*/
|
|
void PStatMonitor::
|
|
got_hello() {
|
|
}
|
|
|
|
/**
|
|
* Like got_hello(), this is called when the "hello" message has been received
|
|
* from the client. At this time, the client's hostname and program name will
|
|
* be known. However, the client appears to be an incompatible version and
|
|
* the connection will be terminated; the monitor should issue a message to
|
|
* that effect.
|
|
*/
|
|
void PStatMonitor::
|
|
got_bad_version(int, int, int, int) {
|
|
}
|
|
|
|
/**
|
|
* Called whenever a new Collector definition is received from the client.
|
|
* Generally, the client will send all of its collectors over shortly after
|
|
* connecting, but there's no guarantee that they will all be received before
|
|
* the first frames are received. The monitor should be prepared to accept
|
|
* new Collector definitions midstream.
|
|
*/
|
|
void PStatMonitor::
|
|
new_collector(int) {
|
|
}
|
|
|
|
/**
|
|
* Called whenever a new Thread definition is received from the client.
|
|
* Generally, the client will send all of its threads over shortly after
|
|
* connecting, but there's no guarantee that they will all be received before
|
|
* the first frames are received. The monitor should be prepared to accept
|
|
* new Thread definitions midstream.
|
|
*/
|
|
void PStatMonitor::
|
|
new_thread(int) {
|
|
}
|
|
|
|
/**
|
|
* Called as each frame's data is made available. There is no guarantee the
|
|
* frames will arrive in order, or that all of them will arrive at all. The
|
|
* monitor should be prepared to accept frames received out-of-order or
|
|
* missing. The use of the PStatFrameData / PStatView objects to report the
|
|
* data will facilitate this.
|
|
*/
|
|
void PStatMonitor::
|
|
new_data(int thread_index, int frame_number) {
|
|
const PStatClientData *client_data = get_client_data();
|
|
|
|
// Don't bother to update the thread data until we know at least something
|
|
// about the collectors and threads.
|
|
if (client_data->get_num_collectors() != 0 &&
|
|
client_data->get_num_threads() != 0) {
|
|
PStatView &view = get_view(thread_index);
|
|
const PStatThreadData *thread_data = view.get_thread_data();
|
|
if (!thread_data->is_empty()) {
|
|
int latest = thread_data->get_latest_frame_number();
|
|
if (frame_number == latest) {
|
|
view.set_to_frame(thread_data->get_frame(frame_number));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when a thread should be removed from the list of threads.
|
|
*/
|
|
void PStatMonitor::
|
|
remove_thread(int) {
|
|
}
|
|
|
|
/**
|
|
* Called whenever the connection to the client has been lost. This is a
|
|
* permanent state change. The monitor should update its display to represent
|
|
* this, and may choose to close down automatically.
|
|
*/
|
|
void PStatMonitor::
|
|
lost_connection() {
|
|
}
|
|
|
|
/**
|
|
* If has_idle() returns true, this will be called periodically to allow the
|
|
* monitor to update its display or whatever it needs to do.
|
|
*/
|
|
void PStatMonitor::
|
|
idle() {
|
|
}
|
|
|
|
/**
|
|
* Should be redefined to return true if you want to redefine idle() and
|
|
* expect it to be called.
|
|
*/
|
|
bool PStatMonitor::
|
|
has_idle() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Should be redefined to return true if this monitor class can handle running
|
|
* in a sub-thread.
|
|
*
|
|
* This is not related to the question of whether it can handle multiple
|
|
* different PStatThreadDatas; this is strictly a question of whether or not
|
|
* the monitor itself wants to run in a sub-thread.
|
|
*/
|
|
bool PStatMonitor::
|
|
is_thread_safe() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Called when the user guide bars have been changed.
|
|
*/
|
|
void PStatMonitor::
|
|
user_guide_bars_changed() {
|
|
}
|
|
|
|
/**
|
|
* Opens a new timeline.
|
|
*/
|
|
PStatGraph *PStatMonitor::
|
|
open_timeline() {
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Opens a new strip chart showing the indicated data.
|
|
*/
|
|
PStatGraph *PStatMonitor::
|
|
open_strip_chart(int thread_index, int collector_index, bool show_level) {
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Opens a new flame graph showing the indicated data.
|
|
*/
|
|
PStatGraph *PStatMonitor::
|
|
open_flame_graph(int thread_index, int collector_index, int frame_number) {
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Opens a new piano roll showing the indicated data.
|
|
*/
|
|
PStatGraph *PStatMonitor::
|
|
open_piano_roll(int thread_index) {
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Writes the client data and open graphs to a datagram.
|
|
*/
|
|
void PStatMonitor::
|
|
write_datagram(Datagram &dg) const {
|
|
dg.add_bool(_client_known);
|
|
dg.add_string(_client_hostname);
|
|
dg.add_string(_client_progname);
|
|
dg.add_int32(_client_pid);
|
|
|
|
get_client_data()->write_datagram(dg);
|
|
|
|
dg.add_uint32((uint32_t)_colors.size());
|
|
for (const auto &item : _colors) {
|
|
dg.add_int32(item.first);
|
|
dg.add_float32(item.second[0]);
|
|
dg.add_float32(item.second[1]);
|
|
dg.add_float32(item.second[2]);
|
|
}
|
|
|
|
dg.add_uint16(_timelines.size());
|
|
for (PStatGraph *graph : _timelines) {
|
|
graph->write_datagram(dg);
|
|
}
|
|
|
|
dg.add_uint16(_strip_charts.size());
|
|
for (PStatGraph *graph : _strip_charts) {
|
|
dg.add_int16(((PStatStripChart *)graph)->get_thread_index());
|
|
dg.add_int16(((PStatStripChart *)graph)->get_collector_index());
|
|
dg.add_bool(((PStatStripChart *)graph)->get_view().get_show_level());
|
|
graph->write_datagram(dg);
|
|
}
|
|
|
|
dg.add_uint16(_flame_graphs.size());
|
|
for (PStatGraph *graph : _flame_graphs) {
|
|
dg.add_int16(((PStatFlameGraph *)graph)->get_thread_index());
|
|
dg.add_int16(((PStatFlameGraph *)graph)->get_collector_index());
|
|
graph->write_datagram(dg);
|
|
}
|
|
|
|
dg.add_uint16(_piano_rolls.size());
|
|
for (PStatGraph *graph : _piano_rolls) {
|
|
dg.add_int16(((PStatPianoRoll *)graph)->get_thread_index());
|
|
graph->write_datagram(dg);
|
|
}
|
|
|
|
// Reserved for future graph type
|
|
dg.add_uint16(0);
|
|
}
|
|
|
|
/**
|
|
* Restores the client data and open graphs from a datagram.
|
|
*/
|
|
void PStatMonitor::
|
|
read_datagram(DatagramIterator &scan) {
|
|
_client_known = scan.get_bool();
|
|
_client_hostname = scan.get_string();
|
|
_client_progname = scan.get_string();
|
|
_client_pid = scan.get_int32();
|
|
|
|
PStatClientData *client_data = new PStatClientData;
|
|
client_data->read_datagram(scan);
|
|
set_client_data(client_data);
|
|
|
|
size_t num_colors = scan.get_uint32();
|
|
for (size_t i = 0; i < num_colors; ++i) {
|
|
int key = scan.get_int32();
|
|
LRGBColor &color = _colors[key];
|
|
color[0] = scan.get_float32();
|
|
color[1] = scan.get_float32();
|
|
color[2] = scan.get_float32();
|
|
}
|
|
|
|
int num_collectors = client_data->get_num_collectors();
|
|
for (int collector_index = 0; collector_index < num_collectors; ++collector_index) {
|
|
if (client_data->has_collector(collector_index)) {
|
|
new_collector(collector_index);
|
|
}
|
|
}
|
|
|
|
int num_threads = client_data->get_num_threads();
|
|
for (int thread_index = 0; thread_index < num_threads; ++thread_index) {
|
|
if (client_data->has_thread(thread_index)) {
|
|
const PStatThreadData *thread_data = client_data->get_thread_data(thread_index);
|
|
if (!thread_data->is_empty()) {
|
|
const PStatFrameData &frame_data = thread_data->get_latest_frame();
|
|
get_view(thread_index).set_to_frame(frame_data);
|
|
|
|
int num_collectors = client_data->get_num_toplevel_collectors();
|
|
for (int i = 0; i < num_collectors; ++i) {
|
|
int collector_index = client_data->get_toplevel_collector(i);
|
|
if (client_data->has_collector(collector_index)) {
|
|
get_level_view(collector_index, thread_index).set_to_frame(thread_data->get_latest_frame());
|
|
}
|
|
}
|
|
}
|
|
new_thread(thread_index);
|
|
}
|
|
}
|
|
|
|
PStatGraph *graph;
|
|
|
|
size_t num_timelines = scan.get_uint16();
|
|
for (size_t i = 0; i < num_timelines; ++i) {
|
|
graph = open_timeline();
|
|
graph->read_datagram(scan);
|
|
}
|
|
|
|
size_t num_strip_charts = scan.get_uint16();
|
|
for (size_t i = 0; i < num_strip_charts; ++i) {
|
|
int thread_index = scan.get_int16();
|
|
int collector_index = scan.get_int16();
|
|
graph = open_strip_chart(thread_index, collector_index, scan.get_bool());
|
|
graph->read_datagram(scan);
|
|
}
|
|
|
|
size_t num_flame_graphs = scan.get_uint16();
|
|
for (size_t i = 0; i < num_flame_graphs; ++i) {
|
|
int thread_index = scan.get_int16();
|
|
int collector_index = scan.get_int16();
|
|
graph = open_flame_graph(thread_index, collector_index);
|
|
graph->read_datagram(scan);
|
|
}
|
|
|
|
size_t num_piano_rolls = scan.get_uint16();
|
|
for (size_t i = 0; i < num_piano_rolls; ++i) {
|
|
int thread_index = scan.get_int16();
|
|
graph = open_piano_roll(thread_index);
|
|
graph->read_datagram(scan);
|
|
}
|
|
}
|