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 }
PStatReader::close
void close()
This will be called by the PStatClientData in response to its close() call.
Definition: pStatReader.cxx:61
PStatServer::release_udp_port
void release_udp_port(int port)
Indicates that the given UDP port is once again free for use.
Definition: pStatServer.cxx:173
PStatServer
The overall manager of the network connections.
Definition: pStatServer.h:36
pStatReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
pStatProperties.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatReader::get_monitor
PStatMonitor * get_monitor()
Returns the monitor that this reader serves.
Definition: pStatReader.cxx:119
CircBuffer::empty
bool empty() const
Returns true if the buffer is empty.
Definition: circBuffer.I:54
CircBuffer::pop_front
void pop_front()
Removes the first item from the buffer.
Definition: circBuffer.I:125
PStatClientControlMessage
This kind of message is sent from the client to the server on the TCP socket to establish critical co...
Definition: pStatClientControlMessage.h:30
PStatReader::idle
void idle()
Called each frame to do what needs to be done for the monitor's user- defined idle routines.
Definition: pStatReader.cxx:110
Connection
Represents a single TCP or UDP socket for input or output.
Definition: connection.h:29
PStatReader::set_tcp_connection
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
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
ConnectionReader::add_connection
bool add_connection(Connection *connection)
Adds a new socket to the list of sockets the ConnectionReader will monitor.
Definition: connectionReader.cxx:176
get_current_pstat_major_version
int get_current_pstat_major_version()
Returns the current major version number of the PStats protocol.
Definition: pStatProperties.cxx:43
PStatClientData
The data associated with a particular client, but not with any one particular frame or thread: the li...
Definition: pStatClientData.h:36
PStatServer::remove_reader
void remove_reader(Connection *connection, PStatReader *reader)
Removes the indicated reader.
Definition: pStatServer.cxx:143
ConnectionWriter::send
bool send(const Datagram &datagram, const PT(Connection) &connection, bool block=false)
Enqueues a datagram for transmittal on the indicated socket.
Definition: connectionWriter.cxx:143
PStatServer::get_udp_port
int get_udp_port()
Returns a new port number that will probably be free to use as a UDP port.
Definition: pStatServer.cxx:160
CircBuffer::full
bool full() const
Returns true if the buffer is full; if this is true, push_back() will fail.
Definition: circBuffer.I:66
PStatReader::lost_connection
void lost_connection()
This is called by the PStatServer when it detects that the connection has been lost.
Definition: pStatReader.cxx:94
get_current_pstat_minor_version
int get_current_pstat_minor_version()
Returns the current minor version number of the PStats protocol.
Definition: pStatProperties.cxx:56
datagram.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatServerControlMessage::encode
void encode(Datagram &datagram) const
Writes the message into the indicated datagram.
Definition: pStatServerControlMessage.cxx:32
pStatServerControlMessage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatMonitor.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NetDatagram
A specific kind of Datagram, especially for sending across or receiving from a network.
Definition: netDatagram.h:40
PStatClientControlMessage::decode
bool decode(const Datagram &datagram, PStatClientVersion *version)
Extracts the message from the indicated datagram.
Definition: pStatClientControlMessage.cxx:74
datagramIterator.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatFrameData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CircBuffer::front
const Thing & front() const
Returns a reference to the first item in the queue.
Definition: circBuffer.I:78
pStatServer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatServerControlMessage
This kind of message is sent from the server to the client on the TCP socket to establish critical co...
Definition: pStatServerControlMessage.h:27
connectionManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ConnectionManager::get_host_name
get_host_name
Returns the name of this particular machine on the network, if available, or the empty string if the ...
Definition: connectionManager.h:111
PStatMonitor
This is an abstract class that presents the interface to any number of different front-ends for the s...
Definition: pStatMonitor.h:39
ConnectionReader
This is an abstract base class for a family of classes that listen for activity on a socket and respo...
Definition: connectionReader.h:50
CircBuffer::push_back
void push_back(const Thing &t)
Adds an item to the end of the buffer.
Definition: circBuffer.I:169
pStatClientControlMessage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatFrameData
Contains the raw timing and level data for a single frame.
Definition: pStatFrameData.h:33