Panda3D
pStatReader.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pStatReader.cxx
10  * @author drose
11  * @date 2000-07-09
12  */
13 
14 #include "pStatReader.h"
15 #include "pStatServer.h"
16 #include "pStatMonitor.h"
17 
20 #include "pStatFrameData.h"
21 #include "pStatProperties.h"
22 #include "datagram.h"
23 #include "datagramIterator.h"
24 #include "connectionManager.h"
25 
26 /**
27  *
28  */
29 PStatReader::
30 PStatReader(PStatServer *manager, PStatMonitor *monitor) :
31 #ifdef HAVE_THREADS
32  ConnectionReader(manager, monitor->is_thread_safe() ? 1 : 0),
33 #else // HAVE_THREADS
34  ConnectionReader(manager, 0),
35 #endif // HAVE_THREADS
36  _manager(manager),
37  _monitor(monitor),
38  _writer(manager, 0)
39 {
40  set_tcp_header_size(4);
41  _writer.set_tcp_header_size(4);
42  _udp_port = 0;
43  _client_data = new PStatClientData(this);
44  _monitor->set_client_data(_client_data);
45 }
46 
47 /**
48  *
49  */
50 PStatReader::
51 ~PStatReader() {
52  _manager->release_udp_port(_udp_port);
53 }
54 
55 /**
56  * This will be called by the PStatClientData in response to its close() call.
57  * It will tell the server to let go of the reader so it can shut down its
58  * connection.
59  */
60 void PStatReader::
61 close() {
62  _manager->remove_reader(_tcp_connection, this);
64 }
65 
66 /**
67  * This is intended to be called only once, immediately after construction, by
68  * the PStatListener that created it. It tells the reader about the newly-
69  * established TCP connection to a client.
70  */
71 void PStatReader::
72 set_tcp_connection(Connection *tcp_connection) {
73  _tcp_connection = tcp_connection;
74  add_connection(_tcp_connection);
75 
76  _udp_port = _manager->get_udp_port();
77  _udp_connection = _manager->open_UDP_connection(_udp_port);
78  while (_udp_connection.is_null()) {
79  // That UDP port was no good. Try another.
80  _udp_port = _manager->get_udp_port();
81  _udp_connection = _manager->open_UDP_connection(_udp_port);
82  }
83 
84  add_connection(_udp_connection);
85 
86  send_hello();
87 }
88 
89 /**
90  * This is called by the PStatServer when it detects that the connection has
91  * been lost. It should clean itself up and shut down nicely.
92  */
93 void PStatReader::
95  _client_data->_is_alive = false;
96  _monitor->lost_connection();
97  _client_data.clear();
98 
99  _manager->close_connection(_tcp_connection);
100  _manager->close_connection(_udp_connection);
101  _tcp_connection.clear();
102  _udp_connection.clear();
103 }
104 
105 /**
106  * Called each frame to do what needs to be done for the monitor's user-
107  * defined idle routines.
108  */
109 void PStatReader::
110 idle() {
111  dequeue_frame_data();
112  _monitor->idle();
113 }
114 
115 /**
116  * Returns the monitor that this reader serves.
117  */
120  return _monitor;
121 }
122 
123 /**
124  * Returns the current machine's hostname.
125  */
126 std::string PStatReader::
127 get_hostname() {
128  if (_hostname.empty()) {
129  _hostname = ConnectionManager::get_host_name();
130  if (_hostname.empty()) {
131  _hostname = "unknown";
132  }
133  }
134  return _hostname;
135 }
136 
137 /**
138  * Sends the initial greeting message to the client.
139  */
140 void PStatReader::
141 send_hello() {
143  message._type = PStatServerControlMessage::T_hello;
144  message._server_hostname = get_hostname();
145  message._server_progname = _monitor->get_monitor_name();
146  message._udp_port = _udp_port;
147 
148  Datagram datagram;
149  message.encode(datagram);
150  _writer.send(datagram, _tcp_connection);
151 }
152 
153 /**
154  * Called by the net code whenever a new datagram is detected on a either the
155  * TCP or UDP connection.
156  */
157 void PStatReader::
158 receive_datagram(const NetDatagram &datagram) {
159  Connection *connection = datagram.get_connection();
160 
161  if (connection == _tcp_connection) {
163  if (message.decode(datagram, _client_data)) {
164  handle_client_control_message(message);
165 
166  } else if (message._type == PStatClientControlMessage::T_datagram) {
167  handle_client_udp_data(datagram);
168 
169  } else {
170  nout << "Got unexpected message from client.\n";
171  }
172 
173  } else if (connection == _udp_connection) {
174  handle_client_udp_data(datagram);
175 
176  } else {
177  nout << "Got datagram from unexpected socket.\n";
178  }
179 }
180 
181 /**
182  * Called when a control message has been received by the client over the TCP
183  * connection.
184  */
185 void PStatReader::
186 handle_client_control_message(const PStatClientControlMessage &message) {
187  switch (message._type) {
188  case PStatClientControlMessage::T_hello:
189  {
190  _client_data->set_version(message._major_version, message._minor_version);
191  int server_major_version = get_current_pstat_major_version();
192  int server_minor_version = get_current_pstat_minor_version();
193 
194  if (message._major_version != server_major_version ||
195  (message._major_version == server_major_version &&
196  message._minor_version > server_minor_version)) {
197  _monitor->bad_version(message._client_hostname, message._client_progname,
198  message._major_version, message._minor_version,
199  server_major_version, server_minor_version);
200  _monitor->close();
201  } else {
202  _monitor->hello_from(message._client_hostname, message._client_progname);
203  }
204  }
205  break;
206 
207  case PStatClientControlMessage::T_define_collectors:
208  {
209  for (int i = 0; i < (int)message._collectors.size(); i++) {
210  _client_data->add_collector(message._collectors[i]);
211  _monitor->new_collector(message._collectors[i]->_index);
212  }
213  }
214  break;
215 
216  case PStatClientControlMessage::T_define_threads:
217  {
218  for (int i = 0; i < (int)message._names.size(); i++) {
219  int thread_index = message._first_thread_index + i;
220  std::string name = message._names[i];
221  _client_data->define_thread(thread_index, name);
222  _monitor->new_thread(thread_index);
223  }
224  }
225  break;
226 
227  default:
228  nout << "Invalid control message received from client.\n";
229  }
230 }
231 
232 /**
233  * Called when a UDP datagram has been received by the client. This should be
234  * a single frame's worth of data.
235  */
236 void PStatReader::
237 handle_client_udp_data(const Datagram &datagram) {
238  if (!_monitor->is_client_known()) {
239  // If we haven't heard a "hello" from the client yet, we don't know what
240  // version data it will be sending us, so we can't decode the data.
241  // Chances are good we can't display it sensibly yet anyway. Ignore frame
242  // data until we get that hello.
243  return;
244  }
245 
246  DatagramIterator source(datagram);
247 
248  if (_client_data->is_at_least(2, 1)) {
249  // Throw away the zero byte at the beginning.
250  int initial_byte = source.get_uint8();
251  nassertv(initial_byte == 0);
252  }
253 
254  if (!_queued_frame_data.full()) {
255  FrameData data;
256  data._thread_index = source.get_uint16();
257  data._frame_number = source.get_uint32();
258  data._frame_data = new PStatFrameData;
259  data._frame_data->read_datagram(source, _client_data);
260 
261  // Queue up the data till we're ready to handle it in a single-threaded
262  // way.
263  _queued_frame_data.push_back(data);
264  }
265 }
266 
267 /**
268  * Called during the idle loop to pull out all the frame data that we might
269  * have read while the threaded reader was running.
270  */
271 void PStatReader::
272 dequeue_frame_data() {
273  while (!_queued_frame_data.empty()) {
274  const FrameData &data = _queued_frame_data.front();
275  nassertv(_client_data != nullptr);
276 
277  // Check to see if any new collectors have level data.
278  int num_levels = data._frame_data->get_num_levels();
279  for (int i = 0; i < num_levels; i++) {
280  int collector_index = data._frame_data->get_level_collector(i);
281  if (!_client_data->get_collector_has_level(collector_index, data._thread_index)) {
282  // This collector is now reporting level data, and it wasn't before.
283  _client_data->set_collector_has_level(collector_index, data._thread_index, true);
284  _monitor->new_collector(collector_index);
285  }
286  }
287 
288  _client_data->record_new_frame(data._thread_index,
289  data._frame_number,
290  data._frame_data);
291  _monitor->new_data(data._thread_index, data._frame_number);
292 
293  _queued_frame_data.pop_front();
294  }
295 }
PStatMonitor * get_monitor()
Returns the monitor that this reader serves.
void encode(Datagram &datagram) const
Writes the message into the indicated datagram.
void push_back(const Thing &t)
Adds an item to the end of the buffer.
Definition: circBuffer.I:169
A specific kind of Datagram, especially for sending across or receiving from a network.
Definition: netDatagram.h:40
bool send(const Datagram &datagram, const PT(Connection) &connection, bool block=false)
Enqueues a datagram for transmittal on the indicated socket.
void set_tcp_connection(Connection *tcp_connection)
This is intended to be called only once, immediately after construction, by the PStatListener that cr...
Definition: pStatReader.cxx:72
bool empty() const
Returns true if the buffer is empty.
Definition: circBuffer.I:54
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This kind of message is sent from the server to the client on the TCP socket to establish critical co...
The data associated with a particular client, but not with any one particular frame or thread: the li...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This kind of message is sent from the client to the server on the TCP socket to establish critical co...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_udp_port()
Returns a new port number that will probably be free to use as a UDP port.
bool full() const
Returns true if the buffer is full; if this is true, push_back() will fail.
Definition: circBuffer.I:66
void idle()
Called each frame to do what needs to be done for the monitor's user- defined idle routines.
int get_current_pstat_minor_version()
Returns the current minor version number of the PStats protocol.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract base class for a family of classes that listen for activity on a socket and respo...
This is an abstract class that presents the interface to any number of different front-ends for the s...
Definition: pStatMonitor.h:39
bool decode(const Datagram &datagram, PStatClientVersion *version)
Extracts the message from the indicated datagram.
void lost_connection()
This is called by the PStatServer when it detects that the connection has been lost.
Definition: pStatReader.cxx:94
void pop_front()
Removes the first item from the buffer.
Definition: circBuffer.I:125
Contains the raw timing and level data for a single frame.
void remove_reader(Connection *connection, PStatReader *reader)
Removes the indicated reader.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_current_pstat_major_version()
Returns the current major version number of the PStats protocol.
bool add_connection(Connection *connection)
Adds a new socket to the list of sockets the ConnectionReader will monitor.
The overall manager of the network connections.
Definition: pStatServer.h:36
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A class to retrieve the individual data elements previously stored in a Datagram.
const Thing & front() const
Returns a reference to the first item in the queue.
Definition: circBuffer.I:78
Represents a single TCP or UDP socket for input or output.
Definition: connection.h:29
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void release_udp_port(int port)
Indicates that the given UDP port is once again free for use.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void close()
This will be called by the PStatClientData in response to its close() call.
Definition: pStatReader.cxx:61