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