YARP
Yet Another Robot Platform
NetworkProfiler.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
7 
8 #include <yarp/os/Network.h>
9 #include <yarp/os/LogStream.h>
10 #include <yarp/os/ContactStyle.h>
11 #include <yarp/os/Port.h>
12 #include <yarp/os/OutputProtocol.h>
13 #include <yarp/os/Carrier.h>
14 #include <yarp/companion/impl/Companion.h>
15 
16 using namespace yarp::os;
17 using namespace yarp::profiler;
18 using namespace yarp::profiler::graph;
19 
20 
21 
22 NetworkProfiler::ProgressCallback* NetworkProfiler::progCallback = nullptr;
23 
24 bool NetworkProfiler::yarpNameList(ports_name_set &ports, bool complete) {
25  ports.clear();
26 
27  ContactStyle style;
28  style.quiet = true;
29  style.timeout = 3.0;
30  std::string nameserver = NetworkBase::getNameServerName();
31  Bottle msg, reply;
32  msg.addString("bot");
33  msg.addString("list");
34  if(!NetworkBase::write(Contact(nameserver), msg, reply, style)) {
35  yError() << "Cannot write to yarp name server";
36  return false;
37  }
38 
39  if(reply.size() == 0) {
40  yError() << "Empty reply from yarp name server";
41  return false;
42  }
43 
44  for (size_t i=1; i<reply.size(); i++) {
45  Bottle *entry = reply.get(i).asList();
46  if(entry != nullptr) {
47  bool shouldTake = false;
48  std::string portname = entry->check("name", Value("")).asString();
49  if(complete)
50  {
51  shouldTake = portname != "";
52  }
53  else
54  {
55  shouldTake = portname != "" && portname != "fallback" && portname != nameserver;
56  }
57  if (shouldTake) {
58  Contact c = Contact::fromConfig(*entry);
59  if (c.getCarrier() != "mcast") {
60  ports.push_back(*entry);
61  }
62  }
63  }
64  }
65 
66  return true;
67 }
68 
69 bool NetworkProfiler::getPortDetails(const std::string& portName, PortDetails& info) {
70 
71  info.name = portName;
72  Port ping;
73  ping.open("/yarpviz");
74  ping.setAdminMode(true);
75  ping.setTimeout(1.0);
76  if(!NetworkBase::connect(ping.getName(), portName)) {
77  yWarning()<<"Cannot connect to"<<portName;
78  ping.close();
79  return false;
80  }
81 
82  // Getting output connections list
83  Bottle cmd, reply;
84  cmd.addString("list"); cmd.addString("out");
85  if(!ping.write(cmd, reply)) {
86  yError()<<"Cannot write (list out) to"<<portName;
87  ping.close();
88  return false;
89  }
90  for(size_t i=0; i<reply.size(); i++) {
91  ConnectionInfo cnn;
92  cnn.name = reply.get(i).asString();
93  Bottle reply2;
94  cmd.clear();
95  cmd.addString("list"); cmd.addString("out"); cmd.addString(cnn.name);
96  if (!ping.write(cmd, reply2)) {
97  yWarning()<<"Cannot write (list out"<<cnn.name<<") to"<<portName;
98  } else {
99  cnn.carrier = reply2.find("carrier").asString();
100  }
101  info.outputs.push_back(cnn);
102  }
103 
104  // Getting input connections list
105  cmd.clear(); reply.clear();
106  cmd.addString("list"); cmd.addString("in");
107  if(!ping.write(cmd, reply)) {
108  yError()<<"Cannot write (list in) to"<<portName;
109  ping.close();
110  return false;
111  }
112  for(size_t i=0; i<reply.size(); i++) {
113  ConnectionInfo cnn;
114  cnn.name = reply.get(i).asString();
115  if (cnn.name != ping.getName()) {
116  info.inputs.push_back(cnn);
117  }
118  }
119 
120  // Getting owner info
121  cmd.clear(); reply.clear();
122  cmd.addString("prop"); cmd.addString("get"); cmd.addString(portName);
123  if(!ping.write(cmd, reply)) {
124  yError()<<"Cannot write (prop get"<<portName<<") to"<<portName;
125  ping.close();
126  return false;
127  }
128 
129  Property* process = reply.find("process").asDict();
130  if (!process) {
131  yWarning()<<"Cannot find 'process' property of port "<<portName;
132  } else {
133  info.owner.name = process->find("name").asString();
134  info.owner.arguments = process->find("arguments").asString();
135  info.owner.pid = process->find("pid").asInt32();
136  info.owner.priority = process->find("priority").asInt32();
137  info.owner.policy = process->find("policy").asInt32();
138  }
139 
140  Property* platform = reply.find("platform").asDict();
141  if (!platform) {
142  yWarning()<<"Cannot find 'platform' property of port "<<portName;
143  } else {
144  info.owner.os = platform->find("os").asString();
145  info.owner.hostname = platform->find("hostname").asString();
146  }
147 
148  ping.close();
149  return true;
150 }
151 
152 
153 bool NetworkProfiler::creatNetworkGraph(ports_detail_set details, yarp::profiler::graph::Graph& graph) {
154 
155  // adding the ports and processor nodes
156  if (NetworkProfiler::progCallback) {
157  NetworkProfiler::progCallback->onProgress(0);
158  }
159 
161  unsigned int itr_count = 0;
162  for(itr = details.begin(); itr!=details.end(); itr++) {
163  PortDetails info = (*itr);
164 
165  // port node
166  PortVertex* port = new PortVertex(info.name);
167  if (!info.inputs.size() && !info.outputs.size()) {
168  port->property.put("orphan", true);
169  }
170  graph.insert(*port);
171 
172  //process node (owner)
173  ProcessVertex* process = new ProcessVertex(info.owner.pid, info.owner.hostname);
174  //prop.clear();
175  process->property.put("name", info.owner.name);
176  process->property.put("arguments", info.owner.arguments);
177  process->property.put("hostname", info.owner.hostname);
178  process->property.put("priority", info.owner.priority);
179  process->property.put("policy", info.owner.policy);
180  process->property.put("os", info.owner.os);
181  process->property.put("hidden", false);
182  auto itrVert=graph.insert(*process);
183  // create connection between ports and its process
184  if (dynamic_cast<ProcessVertex*>(*itrVert)) {
185  port->setOwner((ProcessVertex*)(*itrVert));
186  }
187 
188 
189  //machine node (owner of the process)
190  MachineVertex* machine = new MachineVertex(info.owner.os, info.owner.hostname);
191  graph.insert(*machine);
192  //todo do the same done for the process.
193  process->setOwner(machine);
194 
195  if (!info.inputs.size() && !info.outputs.size()) {
196  graph.insertEdge(*process, *port, Property("(type ownership) (dir unknown)"));
197  }
198 
199  // calculate progress
200  if(NetworkProfiler::progCallback) {
201  NetworkProfiler::progCallback->onProgress((unsigned int) (++itr_count/((float)(details.size()*2)) * 100.0) );
202  }
203  }
204 
205 
206  // create connection between ports
207  for(itr = details.begin(); itr!=details.end(); itr++) {
208  PortDetails info = (*itr);
209  // find the current port vertex in the graph
210  pvertex_iterator vi1 = graph.find(PortVertex(info.name));
211  yAssert(vi1 != graph.vertices().end());
212  for(auto cnn : info.outputs) {
213  pvertex_iterator vi2 = graph.find(PortVertex(cnn.name));
214  if(vi2 != graph.vertices().end()) {
215  //yInfo()<<"connecting "<<(*vi1)->property.find("name").asString()<<"->"<<(*vi2)->property.find("name").asString();
216  Property edge_prop("(type connection)");
217  edge_prop.put("carrier", cnn.carrier);
218  graph.insertEdge(vi1, vi2, edge_prop);
219  } else {
220  yWarning() << "Found a nonexistent port (" << cnn.name << ")"
221  << "in the output list of" << (*vi1)->property.find("name").asString();
222  }
223  }
224  // calculate progress
225  if(NetworkProfiler::progCallback) {
226  NetworkProfiler::progCallback->onProgress((unsigned int) (++itr_count/((float)(details.size()*2)) * 100.0) );
227  }
228  }
229  if (NetworkProfiler::progCallback) {
230  NetworkProfiler::progCallback->onProgress(100); // is it really needed? :p
231  }
232  return true;
233 }
234 
235 bool NetworkProfiler::yarpClean(float timeout) {
236 
237  if (timeout <= 0) {
238  timeout = -1;
239  }
240 
241  std::stringstream sstream;
242  sstream<<timeout;
243  char* argv[2];
244  argv[0] = (char*) "--timeout";
245  argv[1] = (char*) sstream.str().c_str();
246  yarp::companion::impl::Companion::getInstance().cmdClean(2,argv);
247  return true;
248 }
249 
250 bool NetworkProfiler::creatSimpleModuleGraph(yarp::profiler::graph::Graph& graph, yarp::profiler::graph::Graph& subgraph) {
251  subgraph.clear();
253  const pvertex_set& vertices = graph.vertices();
254  //insert machines
255  for(itr = vertices.begin(); itr!=vertices.end(); itr++) {
256 
257  if (!dynamic_cast<MachineVertex*>(*itr)) {
258  continue;
259  } else {
260  auto* mv1 = dynamic_cast<MachineVertex*>(*itr);
261  if (mv1)
262  {
263  MachineVertex* mv2 = new MachineVertex(mv1->property.find("os").asString(),
264  mv1->property.find("hostname").asString());
265  mv2->property = mv1->property;
266  subgraph.insert(*mv2);
267  }
268  }
269  }
270 
271  for(itr = vertices.begin(); itr!=vertices.end(); itr++) {
272  if (!dynamic_cast<ProcessVertex*>(*itr)) {
273  continue;
274  }
275  auto* pv1 = dynamic_cast<ProcessVertex*>(*itr);
276  if (pv1)
277  {
278  ProcessVertex* pv2 = new ProcessVertex(pv1->property.find("pid").asInt32(),
279  pv1->property.find("hostname").asString());
280  pv2->property = pv1->property;
281  subgraph.insert(*pv2);
282  }
283  }
284  // insert edges
285  for(itr = vertices.begin(); itr!=vertices.end(); itr++) {
286  if (!dynamic_cast<ProcessVertex*>(*itr)) {
287  continue;
288  }
289  Vertex* v1 = (*itr);
290  const edge_set& outs = v1->outEdges();
291  edge_const_iterator eitr;
292  for(eitr = outs.begin(); eitr!=outs.end(); eitr++) {
293  const Edge& e = (*eitr);
294  const Vertex& p1 = e.second();
295 
296  const edge_set& pouts = p1.outEdges();
297  edge_const_iterator peitr;
298  for(peitr = pouts.begin(); peitr!=pouts.end(); peitr++) {
299  const Vertex& p2 = (*peitr).second();
300  Property prop((*peitr).property);
301  std::string label = p1.property.find("name").asString();
302  label.append(" - ").append(p2.property.find("name").asString());
303  prop.put("label", label);
304  subgraph.insertEdge(*v1, p2.outEdges()[0].second(), prop);
305  }
306  }
307  }
308  return true;
309 }
310 
311 std::string NetworkProfiler::packetPrioToString(yarp::os::QosStyle::PacketPriorityLevel level) {
312  std::string name;
313  switch(level) {
315  name = "NORMAL";
316  break;
317  }
319  name = "LOW";
320  break;
321  }
323  name = "HIGH";
324  break;
325  }
327  name = "CRITIC";
328  break;
329  }
331  name = "INVALID";
332  break;
333  }
334  default: {
335  name = "UNDEFINED";
336  }
337  };
338  return name;
339 }
340 
341 yarp::os::QosStyle::PacketPriorityLevel NetworkProfiler::packetStringToPrio(std::string level) {
342  if (level == "NORMAL") {
344  }
345  if (level == "LOW") {
347  }
348  if (level == "HIGH") {
350  }
351  if (level == "CRITIC") {
353  }
354  if (level == "INVALID") {
356  }
358 }
359 
360 bool NetworkProfiler::updateConnectionQosStatus(yarp::profiler::graph::Graph& graph) {
361  // adding all process nodes and subgraphs
363  const pvertex_set& vertices = graph.vertices();
364 
365  for(itr = vertices.begin(); itr!=vertices.end(); itr++) {
366  const Vertex &v1 = (**itr);
367  for(const auto& i : v1.outEdges()) {
368  Edge& edge = (Edge&) i;
369  const Vertex &v2 = edge.second();
370  if(!v1.property.check("hidden") && !v2.property.check("hidden")) {
371  if(edge.property.find("type").asString() == "connection") {
372  //yInfo()<<v1.property.find("name").asString()<<"->"<<v2.property.find("name").asString()<<label;
373  yarp::os::QosStyle fromStyle, toStyle;
375  v2.property.find("name").asString(), fromStyle, toStyle)) {
376  // source
377  edge.property.put("FromThreadPriority", fromStyle.getThreadPriority());
378  edge.property.put("FromThreadPolicy", fromStyle.getThreadPolicy());
379  edge.property.put("FromPacketPriority", fromStyle.getPacketPriorityAsLevel());
380  edge.property.put("ToThreadPriority", toStyle.getThreadPriority());
381  edge.property.put("ToThreadPolicy", toStyle.getThreadPolicy());
382  edge.property.put("ToPacketPriority", toStyle.getPacketPriorityAsLevel());
383  } else {
384  yWarning() << "Cannot retrieve Qos property of" << v1.property.find("name").asString() << "->" << v2.property.find("name").asString();
385  }
386  }
387  }
388  }
389  }
390  return true;
391 }
392 
393 bool NetworkProfiler::attachPortmonitorPlugin(std::string portName, yarp::os::Property pluginProp) {
394 
395  //e.g., atch in "(context yarpviz) (file portrate)"
396  yarp::os::Bottle cmd, reply;
397  cmd.addString("atch");
398  cmd.addString("in");
399  cmd.addString(pluginProp.toString());
400  //Property& prop = cmd.addDict();
401  //prop.fromString(pluginProp.toString());
402  //yInfo()<<cmd.toString();
403  Contact srcCon = Contact::fromString(portName);
404  bool ret = yarp::os::NetworkBase::write(srcCon, cmd, reply, true, true, 2.0);
405  if(!ret) {
406  yError()<<"Cannot write to"<<portName;
407  return false;
408  }
409  if(reply.get(0).asString() != "ok") {
410  yError()<<reply.toString();
411  return false;
412  }
413 
414  return true;
415 
416 }
417 
418 bool NetworkProfiler::detachPortmonitorPlugin(std::string portName) {
419  //e.g., dtch in
420  yarp::os::Bottle cmd, reply;
421  cmd.addString("dtch");
422  cmd.addString("in");
423  Contact srcCon = Contact::fromString(portName);
424  bool ret = yarp::os::NetworkBase::write(srcCon, cmd, reply, true, true, 2.0);
425  if(!ret) {
426  yError()<<"Cannot write to"<<portName;
427  return false;
428  }
429  if(reply.get(0).asString() != "ok") {
430  yError()<<reply.toString();
431  return false;
432  }
433  return true;
434 }
435 
436 bool NetworkProfiler::setPortmonitorParams(std::string portName, yarp::os::Property& param) {
437  //e.g., set in "/view" (log_raw 1)"
438  yarp::os::Bottle cmd, reply;
439  cmd.addString("set");
440  cmd.addString("in");
441  cmd.addString(portName.c_str());
442  Bottle tmp;
443  tmp.fromString(param.toString());
444  cmd.add(tmp.get(0));
445  Contact srcCon = Contact::fromString(portName);
446  bool ret = yarp::os::NetworkBase::write(srcCon, cmd, reply, true, true, 2.0);
447  if(!ret) {
448  yError()<<"Cannot write to"<<portName;
449  return false;
450  }
451  if(reply.size() > 1) {
452  if(reply.get(0).isString() && reply.get(0).asString() == "fail") {
453  yError()<<reply.toString();
454  return false;
455  }
456  else if(reply.get(0).isInt32() && reply.get(0).asInt32() == -1) {
457  yError()<<reply.toString();
458  return false;
459  }
460  }
461  return true;
462 }
463 
464 bool NetworkProfiler::getPortmonitorParams(std::string portName, yarp::os::Bottle& param) {
465  //e.g., get in /portname"
466  yarp::os::Bottle cmd;
467  cmd.addString("get");
468  cmd.addString("in");
469  cmd.addString(portName.c_str());
470  Contact srcCon = Contact::fromString(portName);
471  bool ret = yarp::os::NetworkBase::write(srcCon, cmd, param, true, true, 2.0);
472  if(!ret) {
473  yError()<<"Cannot write to"<<portName;
474  return false;
475  }
476  if(param.size() > 1) {
477  if(param.get(0).isString() && param.get(0).asString() == "fail") {
478  yError()<<param.toString();
479  return false;
480  }
481  else if(param.get(0).isInt32() && param.get(0).asInt32() == -1) {
482  yError()<<param.toString();
483  return false;
484  }
485  }
486  return true;
487 }
edge_set::const_iterator edge_const_iterator
Definition: Graph.h:35
pvertex_set::const_iterator pvertex_const_iterator
Definition: Graph.h:39
std::vector< yarp::profiler::graph::Vertex * > pvertex_set
Definition: Graph.h:37
pvertex_set::iterator pvertex_iterator
Definition: Graph.h:38
std::vector< yarp::profiler::graph::Edge > edge_set
Definition: Graph.h:33
bool ret
#define yError(...)
Definition: Log.h:279
#define yAssert(x)
Definition: Log.h:294
#define yWarning(...)
Definition: Log.h:268
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
void add(const Value &value)
Add a Value to the bottle, at the end of the list.
Definition: Bottle.cpp:336
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:204
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:251
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Bottle.cpp:277
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:121
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition: Bottle.cpp:211
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Bottle.cpp:287
Preferences for how to communicate with a contact.
Definition: ContactStyle.h:24
double timeout
Set a timeout for communication (in units of seconds, fractional seconds allowed).
Definition: ContactStyle.h:47
bool quiet
Suppress all outputs and warnings.
Definition: ContactStyle.h:36
Represents how to reach a part of a YARP network.
Definition: Contact.h:36
static Contact fromConfig(const Searchable &config)
Factory method.
Definition: Contact.cpp:129
static Contact fromString(const std::string &txt)
Factory method.
Definition: Contact.cpp:139
std::string getCarrier() const
Get the carrier associated with this Contact for socket communication.
Definition: Contact.cpp:250
virtual std::string getName() const
Get name of port.
Definition: Contactable.cpp:14
static std::string getNameServerName()
Get the name of the port associated with the nameserver (usually "/root", but this can be overwritten...
Definition: Network.cpp:1352
static bool connect(const std::string &src, const std::string &dest, const std::string &carrier="", bool quiet=true)
Request that an output port connect to an input port.
Definition: Network.cpp:682
static bool getConnectionQos(const std::string &src, const std::string &dest, QosStyle &srcStyle, QosStyle &destStyle, bool quiet=true)
Gets the Qos preferences of a connection.
Definition: Network.cpp:1189
static bool write(const Contact &contact, PortWriter &cmd, PortReader &reply, bool admin=false, bool quiet=false, double timeout=-1)
Send a single command to a port and await a single response.
Definition: Network.cpp:1226
A mini-server for network communication.
Definition: Port.h:47
bool write(const PortWriter &writer, const PortWriter *callback=nullptr) const override
Write an object to the port.
Definition: Port.cpp:427
void setAdminMode(bool adminMode=true)
Turn on/off "admin" mode.
Definition: Port.cpp:586
bool setTimeout(float timeout)
Set a timeout on network operations.
Definition: Port.cpp:625
void close() override
Stop port activity.
Definition: Port.cpp:354
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: Port.cpp:79
A class for storing options and configuration information.
Definition: Property.h:34
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Property.cpp:1051
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Property.cpp:1069
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:1015
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Property.cpp:1041
Preferences for the port's Quality of Service.
Definition: QosStyle.h:24
int getThreadPolicy() const
returns the communication thread scheduling policy
Definition: QosStyle.h:182
int getThreadPriority() const
returns the communication thread priority level
Definition: QosStyle.h:172
PacketPriorityLevel
The PacketPriorityLevel defines the packets quality of service (priority) levels.
Definition: QosStyle.h:31
PacketPriorityLevel getPacketPriorityAsLevel() const
returns the packet TOS value
Definition: QosStyle.cpp:131
A single value (typically within a Bottle).
Definition: Value.h:45
virtual bool isString() const
Checks if value is a string.
Definition: Value.cpp:156
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:204
virtual Bottle * asList() const
Get list value.
Definition: Value.cpp:240
virtual Property * asDict() const
Get dictionary (hash table) value.
Definition: Value.cpp:246
virtual bool isInt32() const
Checks if value is a 32-bit integer.
Definition: Value.cpp:132
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
virtual void onProgress(unsigned int percentage)
std::vector< yarp::os::Bottle > ports_name_set
ports_detail_set::iterator ports_detail_iterator
std::vector< PortDetails > ports_detail_set
The yarp::profiler::graph::Edge class.
Definition: Graph.h:49
const yarp::profiler::graph::Vertex & second() const
Definition: Graph.cpp:44
yarp::os::Property property
Definition: Graph.h:65
The yarp::profiler::graph::Graph class.
Definition: Graph.h:108
void insertEdge(const Vertex &v1, const Vertex &v2, const yarp::os::Property &property="")
Definition: Graph.cpp:144
const pvertex_set & vertices()
Definition: Graph.h:130
const pvertex_iterator find(const Vertex &v1)
Definition: Graph.cpp:160
pvertex_iterator insert(const Vertex &vertex)
Definition: Graph.cpp:120
bool setOwner(yarp::profiler::graph::Vertex *_owner)
Definition: Graph.h:170
The yarp::profiler::graph::Vertex class.
Definition: Graph.h:76
yarp::os::Property property
Definition: Graph.h:93
const edge_set & outEdges() const
Definition: Graph.h:83
An interface to the operating system, including Port based communication.
std::vector< ConnectionInfo > inputs
std::vector< ConnectionInfo > outputs