this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2/* 3 * Main authors: 4 * Maxim Shishmarev <maxim.shishmarev@monash.edu> 5 * 6 * Contributing authors: 7 * Kevin Leo <kevin.leo@monash.edu> 8 * Christian Schulte <schulte@gecode.org> 9 * 10 * Copyright: 11 * Kevin Leo, 2017 12 * Christian Schulte, 2017 13 * Maxim Shishmarev, 2017 14 * 15 * This file is part of Gecode, the generic constraint 16 * development environment: 17 * http://www.gecode.org 18 * 19 * Permission is hereby granted, free of charge, to any person obtaining 20 * a copy of this software and associated documentation files (the 21 * "Software"), to deal in the Software without restriction, including 22 * without limitation the rights to use, copy, modify, merge, publish, 23 * distribute, sublicense, and/or sell copies of the Software, and to 24 * permit persons to whom the Software is furnished to do so, subject to 25 * the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be 28 * included in all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 31 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 33 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 34 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 35 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 36 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 * 38 */ 39 40#include <iostream> 41#include <sstream> 42#include <vector> 43#include <cstring> 44 45#ifdef WIN32 46 47#include <winsock2.h> 48#include <ws2tcpip.h> 49#pragma comment(lib, "Ws2_32.lib") 50#pragma comment(lib, "Mswsock.lib") 51#pragma comment(lib, "AdvApi32.lib") 52 53#include <basetsd.h> 54typedef SSIZE_T ssize_t; 55 56#else 57 58#include <netdb.h> 59#include <unistd.h> 60#include <sys/socket.h> 61 62#endif 63 64namespace Gecode { namespace CPProfiler { 65 66 class Node { 67 NodeUID node_; 68 NodeUID parent_; 69 int alt_; 70 int kids_; 71 72 NodeStatus status_; 73 74 Option<std::string> label_; 75 Option<std::string> nogood_; 76 Option<std::string> info_; 77 78 public: 79 Node(NodeUID node, NodeUID parent, 80 int alt, int kids, NodeStatus status); 81 Node& set_node_thread_id(int tid); 82 const Option<std::string>& label() const; 83 Node& set_label(const std::string& label); 84 const Option<std::string>& nogood() const; 85 Node& set_nogood(const std::string& nogood); 86 const Option<std::string>& info() const; 87 Node& set_info(const std::string& info); 88 int alt() const; 89 int kids() const; 90 NodeStatus status() const; 91 NodeUID nodeUID() const; 92 NodeUID parentUID() const; 93 int node_id() const; 94 int parent_id() const; 95 int node_thread_id() const; 96 int node_restart_id() const; 97 int parent_thread_id() const; 98 int parent_restart_id() const; 99 }; 100 101 class Connector { 102 private: 103 MessageMarshalling marshalling; 104 105 const unsigned int port; 106 107 int sockfd; 108 bool _connected; 109 110 static int sendall(int s, const char* buf, int* len); 111 void sendOverSocket(void); 112 void sendRawMsg(const std::vector<char>& buf); 113 public: 114 Connector(unsigned int port); 115 116 bool connected() const; 117 118 /// connect to a socket via port specified in the construction (6565 by 119 /// default) 120 void connect(void); 121 122 // sends START_SENDING message to the Profiler with a model name 123 void start(const std::string& file_path = "", 124 int execution_id = -1, bool has_restarts = false); 125 void restart(int restart_id = -1); 126 void done(); 127 128 /// disconnect from a socket 129 void disconnect(void); 130 131 void sendNode(const Node& node); 132 Node createNode(NodeUID node, NodeUID parent, 133 int alt, int kids, NodeStatus status); 134 }; 135 136 137 /* 138 * Nodes 139 */ 140 inline 141 Node::Node(NodeUID node, NodeUID parent, 142 int alt, int kids, NodeStatus status) 143 : node_{node}, parent_{parent}, 144 alt_(alt), kids_(kids), status_(status) {} 145 146 inline Node& 147 Node::set_node_thread_id(int tid) { 148 node_.tid = tid; 149 return *this; 150 } 151 152 inline const Option<std::string>& 153 Node::label() const { return label_; } 154 155 inline Node& 156 Node::set_label(const std::string& label) { 157 label_.set(label); 158 return *this; 159 } 160 161 inline const Option<std::string>& 162 Node::nogood() const { 163 return nogood_; 164 } 165 166 inline Node& 167 Node::set_nogood(const std::string& nogood) { 168 nogood_.set(nogood); 169 return *this; 170 } 171 172 inline const Option<std::string>& 173 Node::info() const { return info_; } 174 175 inline Node& 176 Node::set_info(const std::string& info) { 177 info_.set(info); 178 return *this; 179 } 180 181 inline int 182 Node::alt() const { return alt_; } 183 inline int 184 Node::kids() const { return kids_; } 185 186 inline NodeStatus 187 Node::status() const { return status_; } 188 189 inline NodeUID 190 Node::nodeUID() const { return node_; } 191 inline NodeUID 192 Node::parentUID() const { return parent_; } 193 194 inline int 195 Node::node_id() const { return node_.nid; } 196 inline int 197 Node::parent_id() const { return parent_.nid; } 198 inline int 199 Node::node_thread_id() const { return node_.tid; } 200 inline int 201 Node::node_restart_id() const { return node_.rid; } 202 inline int 203 Node::parent_thread_id() const { return parent_.tid; } 204 inline int 205 Node::parent_restart_id() const { return parent_.rid; } 206 207 208 /* 209 * Connector 210 */ 211 inline 212 Connector::Connector(unsigned int port) : port(port), _connected(false) {} 213 214 inline bool Connector::connected() const { return _connected; } 215 216 217 /* 218 * The following code is taken from: 219 * Beej's Guide to Network Programming 220 * http://beej.us/guide/bgnet/ 221 * with the folloiwng license: 222 * 223 * Beej's Guide to Network Programming is Copyright © 2015 Brian "Beej Jorgensen" Hall. 224 * With specific exceptions for source code and translations, below, this work is licensed under the Creative Commons Attribution- Noncommercial- No Derivative Works 3.0 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. 225 * One specific exception to the "No Derivative Works" portion of the license is as follows: this guide may be freely translated into any language, provided the translation is accurate, and the guide is reprinted in its entirety. The same license restrictions apply to the translation as to the original guide. The translation may also include the name and contact information for the translator. 226 * The C source code presented in this document is hereby granted to the public domain, and is completely free of any license restriction. 227 * Educators are freely encouraged to recommend or supply copies of this guide to their students. 228 * Contact beej@beej.us for more information. 229 */ 230 inline int 231 Connector::sendall(int s, const char* buf, int* len) { 232 int total = 0; // how many bytes we've sent 233 int bytesleft = *len; // how many we have left to send 234 ssize_t n; 235 236 while (total < *len) { 237 n = send(s, buf + total, static_cast<size_t>(bytesleft), 0); 238 if (n == -1) { 239 break; 240 } 241 total += static_cast<int>(n); 242 bytesleft -= static_cast<int>(n); 243 } 244 245 *len = static_cast<int>(total); // return number actually sent here 246 247 return (n == -1) ? -1 : 0; // return -1 on failure, 0 on success 248 } 249 250 inline void 251 Connector::sendRawMsg(const std::vector<char>& buf) { 252 uint32_t bufSize = static_cast<uint32_t>(buf.size()); 253 int bufSizeLen = sizeof(uint32_t); 254 sendall(sockfd, reinterpret_cast<char*>(&bufSize), &bufSizeLen); 255 int bufSizeInt = static_cast<int>(bufSize); 256 sendall(sockfd, reinterpret_cast<const char*>(buf.data()), &bufSizeInt); 257 } 258 259 inline void 260 Connector::sendOverSocket(void) { 261 if (!_connected) return; 262 263 std::vector<char> buf = marshalling.serialize(); 264 265 sendRawMsg(buf); 266 } 267 268 inline void 269 Connector::connect(void) { 270 struct addrinfo hints, *servinfo, *p; 271 int rv; 272 273#ifdef WIN32 274 // Initialise Winsock. 275 WSADATA wsaData; 276 int startupResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 277 if (startupResult != 0) { 278 printf("WSAStartup failed with error: %d\n", startupResult); 279 } 280#endif 281 282 memset(&hints, 0, sizeof hints); 283 hints.ai_family = AF_UNSPEC; 284 hints.ai_socktype = SOCK_STREAM; 285 286 if ((rv = getaddrinfo("localhost", std::to_string(port).c_str(), &hints, 287 &servinfo)) != 0) { 288 std::cerr << "getaddrinfo: " << gai_strerror(rv) << "\n"; 289 goto giveup; 290 } 291 292 // loop through all the results and connect to the first we can 293 for (p = servinfo; p != nullptr; p = p->ai_next) { 294 if ((sockfd = static_cast<int>(socket(p->ai_family, p->ai_socktype, p->ai_protocol))) == -1) { 295 // errno is set here, but we don't examine it. 296 continue; 297 } 298 299 if (::connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { 300#ifdef WIN32 301 closesocket(sockfd); 302#else 303 close(sockfd); 304#endif 305 // errno is set here, but we don't examine it. 306 continue; 307 } 308 309 break; 310 } 311 312 // Connection failed; give up. 313 if (p == nullptr) { 314 goto giveup; 315 } 316 317 freeaddrinfo(servinfo); // all done with this structure 318 319 _connected = true; 320 321 return; 322 giveup: 323 _connected = false; 324 return; 325 326 } 327 328 inline void 329 Connector::start(const std::string& file_path, 330 int execution_id, bool has_restarts) { 331 /// extract fzn file name 332 std::string base_name(file_path); 333 { 334 size_t pos = base_name.find_last_of('/'); 335 if (pos != static_cast<size_t>(-1)) { 336 base_name = base_name.substr(pos + 1, base_name.length() - pos - 1); 337 } 338 } 339 340 std::string info{""}; 341 { 342 std::stringstream ss; 343 ss << "{"; 344 ss << "\"has_restarts\": " << (has_restarts ? "true" : "false") << "\n"; 345 ss << ",\"name\": " << "\"" << base_name << "\"" << "\n"; 346 if (execution_id != -1) { 347 ss << ",\"execution_id\": " << execution_id; 348 } 349 ss << "}"; 350 info = ss.str(); 351 } 352 353 marshalling.makeStart(info); 354 sendOverSocket(); 355 } 356 357 inline void 358 Connector::restart(int restart_id) { 359 360 std::string info{""}; 361 { 362 std::stringstream ss; 363 ss << "{"; 364 ss << "\"restart_id\": " << restart_id << "\n"; 365 ss << "}"; 366 info = ss.str(); 367 } 368 369 marshalling.makeRestart(info); 370 sendOverSocket(); 371 } 372 373 inline void 374 Connector::done() { 375 marshalling.makeDone(); 376 sendOverSocket(); 377 } 378 379 inline void 380 Connector::disconnect() { 381#ifdef WIN32 382 closesocket(sockfd); 383#else 384 close(sockfd); 385#endif 386 } 387 388 inline void 389 Connector::sendNode(const Node& node) { 390 if (!_connected) return; 391 392 auto& msg = marshalling.makeNode(node.nodeUID(), node.parentUID(), 393 node.alt(), node.kids(), node.status()); 394 395 if (node.label().valid()) msg.set_label(node.label().value()); 396 if (node.nogood().valid()) msg.set_nogood(node.nogood().value()); 397 if (node.info().valid()) msg.set_info(node.info().value()); 398 399 sendOverSocket(); 400 } 401 402 inline Node 403 Connector::createNode(NodeUID node, NodeUID parent, 404 int alt, int kids, NodeStatus status) { 405 return Node(node, parent, alt, kids, status); 406 } 407 408}} 409 410// STATISTICS: search-trace