ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
Utilities.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include "Utilities.h"
5
6#include "BackendFactory.h"
7#include "DMapFileParser.h"
8
9#include <boost/algorithm/string.hpp>
10#include <boost/algorithm/string/predicate.hpp>
11#include <boost/filesystem.hpp>
12
13#include <cxxabi.h>
14#include <execinfo.h>
15
16#include <utility>
17#include <vector>
18
19namespace ChimeraTK {
20
21 /********************************************************************************************************************/
22
23 size_t Utilities::countOccurence(std::string theString, char delimiter) {
24 size_t count = std::count(theString.begin(), theString.end(), delimiter);
25 return count;
26 }
27
28 /********************************************************************************************************************/
29
30 bool Utilities::isSdm(const std::string& theString) {
31 size_t signatureLen = 6;
32 if(theString.length() < signatureLen) return false;
33 if(theString.substr(0, 6) != "sdm://") return false;
34 return true;
35 }
36
37 /********************************************************************************************************************/
38
39 bool Utilities::isDeviceDescriptor(std::string theString) {
40 boost::trim(theString);
41 if(theString.length() < 3) return false;
42 if(theString.substr(0, 1) != "(") return false;
43 if(theString.substr(theString.length() - 1, 1) != ")") return false;
44 return true;
45 }
46
47 /********************************************************************************************************************/
48
50 DeviceDescriptor result;
51
52 // first trim the string to remove whitespaces before and after the outer
53 // parentheses
54 boost::trim(cddString);
55
56 // simple initial checks
57 if(cddString.length() < 3) {
58 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (too short): " + cddString);
59 }
60 if(cddString.substr(0, 1) != "(") {
61 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (missing opening parenthesis): " + cddString);
62 }
63 if(cddString.substr(cddString.length() - 1, 1) != ")") {
64 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (missing opening parenthesis): " + cddString);
65 }
66
67 // walk through string
68 size_t parenthesesLevel = 0;
69 enum class tokenType { backendType, address, parameters };
70 tokenType currentTokenType = tokenType::backendType;
71 bool escapeNext = false;
72 std::string token;
73 size_t positionPlusOne = 0;
74 for(auto& c : cddString) {
75 ++positionPlusOne;
76 // are we indide the outer main parenthesis but not within a deeper level?
77 if(parenthesesLevel == 1) {
78 // should the current character be escaped?
79 if(escapeNext) {
80 if(c == ' ' || c == '?' || c == '&' || c == '(' || c == ')' || c == '\\') {
81 token += c;
82 }
83 else {
84 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (bad escape character): " + cddString);
85 }
86 escapeNext = false;
87 continue; // no further parsing of this character!
88 }
89 // current character should be parsed normally
90 if(c == '\\') {
91 // escape next character
92 escapeNext = true;
93 }
94 else if(currentTokenType == tokenType::backendType && (c == ':' || c == '?' || c == ')')) {
95 // backendType token complete
96 boost::trim(token);
97 if(token.length() == 0) {
98 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor "
99 "(backend type must be non-empty): " +
100 cddString);
101 }
102 if(!boost::all(token, boost::is_alnum())) {
103 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor "
104 "(backend type must be alphanumeric): " +
105 cddString);
106 }
107 result.backendType = token;
108 token = "";
109 // determine next token type by checking the delimiter (')' will be
110 // processed below)
111 if(c == ':') {
112 currentTokenType = tokenType::address;
113 }
114 else if(c == '?') {
115 currentTokenType = tokenType::parameters;
116 }
117 }
118 else if(currentTokenType == tokenType::address && (c == '?' || c == ')')) {
119 // address token complete
120 boost::trim(token);
121 result.address = token;
122 token = "";
123 currentTokenType = tokenType::parameters; // ')' will be processed below
124 }
125 else if(currentTokenType == tokenType::parameters && (c == '&' || c == ')')) {
126 // parameter token complete (for one key-value pair)
127 boost::trim(token);
128 if(token.length() > 0) { // ignore empty parameter
129 auto equalSign = token.find_first_of('=');
130 if(equalSign == std::string::npos) {
131 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (parameters must be "
132 "specified as key=value pairs): " +
133 cddString);
134 }
135 auto key = token.substr(0, equalSign);
136 auto value = token.substr(equalSign + 1);
137 boost::trim(key);
138 if(key.length() == 0) {
139 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (parameter key names must "
140 "not be empty): " +
141 cddString);
142 }
143 if(!boost::all(key, boost::is_alnum())) {
144 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (parameter key names must "
145 "contain only "
146 "alphanumeric characters): " +
147 cddString);
148 }
149 boost::trim(value);
150 if(result.parameters.find(key) != result.parameters.end()) {
151 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (parameter '" + key +
152 "' specified multiple times): " + cddString);
153 }
154 result.parameters[key] = value;
155 token = "";
156 }
157 }
158 else {
159 token += c;
160 }
161 }
162 else if(parenthesesLevel > 1) {
163 // deeper level: add all characters to current token
164 token += c;
165 }
166 if(c == '(') {
167 ++parenthesesLevel;
168 }
169 else if(c == ')') {
170 --parenthesesLevel;
171 if(parenthesesLevel == 0 && positionPlusOne != cddString.length()) {
172 // main parenthesis closed but not yet end of the string
173 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (additional characters after "
174 "last closing parenthesis): " +
175 cddString);
176 }
177 }
178 }
179
180 // check if final parenthesis found
181 if(parenthesesLevel != 0) {
182 throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (unmatched parenthesis): " + cddString);
183 }
184
185 // return the result
186 return result;
187 }
188
189 /********************************************************************************************************************/
190
191 Sdm Utilities::parseSdm(const std::string& sdmString) {
192 Sdm sdmInfo;
193 size_t signatureLen = 6;
194 if(sdmString.length() < signatureLen) throw ChimeraTK::logic_error("Invalid sdm.");
195 if(sdmString.substr(0, 6) != "sdm://") throw ChimeraTK::logic_error("Invalid sdm.");
196 int pos = 6;
197
198 std::size_t found = sdmString.find_first_of('/', pos);
199 std::string subUri;
200 if(found != std::string::npos) {
201 sdmInfo.host = sdmString.substr(pos, found - pos); // Get the Host
202 }
203 else {
204 throw ChimeraTK::logic_error("Invalid sdm.");
205 }
206 if(sdmString.length() < found + 1) return sdmInfo;
207 subUri = sdmString.substr(found + 1);
208 // let's do a sanity check, only one delimiter occurrence is allowed.
209 if(countOccurence(subUri, ':') > 1) /* check ':' */
210 {
211 throw ChimeraTK::logic_error("Invalid sdm.");
212 }
213 if(countOccurence(subUri, ';') > 1) /* check ';' */
214 {
215 throw ChimeraTK::logic_error("Invalid sdm.");
216 }
217 if(countOccurence(subUri, '=') > 1) /* check '=' */
218 {
219 throw ChimeraTK::logic_error("Invalid sdm.");
220 }
221 std::vector<std::string> tokens;
222 boost::split(tokens, subUri, boost::is_any_of(":;="));
223 size_t numOfTokens = tokens.size();
224 if(numOfTokens < 1) return sdmInfo;
225 size_t counter = 0;
226 sdmInfo.interface = tokens[counter]; // Get the Interface
227 counter++;
228 if(counter < numOfTokens) {
229 // Get the Instance
230 found = sdmString.find_first_of(':', pos);
231 if(found != std::string::npos) {
232 sdmInfo.instance = tokens[counter];
233 counter++;
234 }
235 }
236 if(counter < numOfTokens) {
237 // Get the Protocol
238 found = sdmString.find_first_of(';', pos);
239 if(found != std::string::npos) {
240 sdmInfo.protocol = tokens[counter];
241 counter++;
242 }
243 }
244 if(counter < numOfTokens) {
245 // Get the Parameters
246 found = sdmString.find_first_of('=', pos);
247 if(found != std::string::npos) {
248 std::string parameters = tokens[counter];
249 std::vector<std::string> paramterTokens;
250 boost::split(paramterTokens, parameters, boost::is_any_of(","));
251 for(auto& paramterToken : paramterTokens) {
252 sdmInfo.parameters.push_back(paramterToken);
253 }
254 }
255 }
256
257 return sdmInfo;
258 }
259
260 /********************************************************************************************************************/
261
262 Sdm Utilities::parseDeviceString(const std::string& deviceString) {
263 Sdm sdmInfo;
264 if(deviceString.substr(0, 5) == "/dev/") {
265 sdmInfo.interface = "pci";
266 if(deviceString.length() > 5) {
267 sdmInfo.instance = deviceString.substr(5);
268 }
269 }
270 else if((boost::ends_with(deviceString, ".map")) || (boost::ends_with(deviceString, ".mapp"))) {
271 sdmInfo.interface = "dummy";
272 sdmInfo.instance = deviceString;
273 /*another change in interface requires now instance
274 to be ignored and old expect old Instance parameter
275 as firt item of the Parameters list*/
276 sdmInfo.parameters.push_back(sdmInfo.instance);
277 }
278 else {
279 return sdmInfo;
280 }
281 sdmInfo.host = ".";
282 sdmInfo.protocol = "";
283 return sdmInfo;
284 }
285
286 /********************************************************************************************************************/
287
288 DeviceInfoMap::DeviceInfo Utilities::aliasLookUp(const std::string& aliasName, const std::string& dmapFilePath) {
289 DeviceInfoMap::DeviceInfo deviceInfo;
290 auto deviceInfoPointer = ChimeraTK::DMapFileParser::parse(dmapFilePath);
291 deviceInfoPointer->getDeviceInfo(aliasName, deviceInfo);
292 return deviceInfo;
293 }
294
295 /********************************************************************************************************************/
296
297 std::vector<std::string> Utilities::getAliasList() {
298 std::string dmapFileName = getDMapFilePath();
299 if(dmapFileName.empty()) {
300 throw ChimeraTK::logic_error("Dmap file not set");
301 }
302
303 try {
304 auto deviceInfoMap = ChimeraTK::DMapFileParser::parse(dmapFileName);
305
306 std::vector<std::string> listOfDeviceAliases;
307 listOfDeviceAliases.reserve(deviceInfoMap->getSize());
308
309 for(auto&& deviceInfo : *deviceInfoMap) {
310 listOfDeviceAliases.push_back(deviceInfo.deviceName);
311 }
312
313 return listOfDeviceAliases;
314 }
315 catch(ChimeraTK::runtime_error& e) {
316 std::cout << e.what() << std::endl;
317 return {}; // empty list in case of failure
318 }
319 }
320
321 /********************************************************************************************************************/
322
323 std::string getDMapFilePath() {
325 }
326
327 /********************************************************************************************************************/
328
329 void setDMapFilePath(std::string dmapFilePath) {
330 BackendFactory::getInstance().setDMapFilePath(std::move(dmapFilePath));
331 }
332
333 /********************************************************************************************************************/
334
336 void* trace[16];
337 char** messages;
338 int i, trace_size = 0;
339
340 trace_size = backtrace(trace, 16);
341 messages = backtrace_symbols(trace, trace_size);
342 printf("[bt] Execution path:\n");
343 for(i = 0; i < trace_size; ++i) {
344 std::string msg(messages[i]);
345 size_t a = msg.find_first_of('(');
346 size_t b = msg.find_first_of('+');
347 std::string functionName = boost::core::demangle(msg.substr(a + 1, b - a - 1).c_str());
348 std::cout << "[bt] #" << i << " " << functionName << std::endl;
349 }
350 }
351
352 /********************************************************************************************************************/
353
355 const std::string& address, const std::map<std::string, std::string>& parameters) {
356 // To find hash, concatenate in ordered way, address and parameters
357 std::string str = address;
358 for(const auto& e : parameters) {
359 str += e.first + "=" + e.second + ";";
360 }
361 return std::hash<std::string>{}(str);
362 }
363
364 /********************************************************************************************************************/
365
367 std::size_t instanceIdHash, const std::string& mapFileName, const std::string& userName) {
368 // always use absolute mapFileName
369 boost::filesystem::path absPathToMapFile = boost::filesystem::absolute(mapFileName);
370
371 std::string mapFileHash{std::to_string(std::hash<std::string>{}(absPathToMapFile.string()))};
372 std::string userHash{std::to_string(std::hash<std::string>{}(userName))};
373
374 return "ChimeraTK_SharedDummy_" + std::to_string(instanceIdHash) + "_" + mapFileHash + "_" + userHash;
375 }
376
377 /********************************************************************************************************************/
378
379} /* namespace ChimeraTK */
static BackendFactory & getInstance()
Static function to get an instance of factory.
void setDMapFilePath(std::string dMapFilePath)
This function sets the _DMapFilePath.
static DeviceInfoMapPointer parse(const std::string &file_name)
Performs parsing of specified DMAP file.
Stores information about one device.
Exception thrown when a logic error has occured.
Definition Exception.h:51
Exception thrown when a runtime error has occured.
Definition Exception.h:18
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Definition Exception.cpp:14
DeviceDescriptor parseDeviceDesciptor(std::string cddString)
Parse a ChimeraTK device descriptor (CDD) and return the information in the DeviceDescriptor struct.
Definition Utilities.cpp:49
std::vector< std::string > getAliasList()
Returns the list of device aliases from the DMap file set using BackendFactory::setDMapFilePath.
void printStackTrace()
Print a call stack trace (but continue executing the process normally).
DeviceInfoMap::DeviceInfo aliasLookUp(const std::string &aliasName, const std::string &dmapFilePath)
Search for an alias in a given DMap file and return the DeviceInfo entry.
std::size_t shmDummyInstanceIdHash(const std::string &address, const std::map< std::string, std::string > &parameters)
Generates shm dummy instanceId hash from address and parameter map, Intended for use with parseDevice...
bool isSdm(const std::string &theString)
Check wehter the given string seems to be an SDM.
Definition Utilities.cpp:30
size_t countOccurence(std::string theString, char delimiter)
Check if the given string only contains alphanumeric characters.
Definition Utilities.cpp:23
Sdm parseDeviceString(const std::string &deviceString)
Parse an old-style device string (either path to device node or map file name for dummies)
Sdm parseSdm(const std::string &sdmString)
Parse an SDM URI and return the device information in the Sdm struct.
std::string createShmName(std::size_t instanceIdHash, const std::string &mapFileName, const std::string &userName)
Generates shm dummy name from parameter hashes.
bool isDeviceDescriptor(std::string theString)
Check whether the given string seems to be a CDD.
Definition Utilities.cpp:39
std::string getDMapFilePath()
Returns the dmap file name which the library currently uses for looking up device(alias) names.
void setDMapFilePath(std::string dmapFilePath)
Set the location of the dmap file.
std::string to_string(const std::string &v)
This structure holds the information of an ChimeraTK device descriptor.
Definition Utilities.h:31
std::map< std::string, std::string > parameters
Definition Utilities.h:34
This structure holds the information of an SDM.
Definition Utilities.h:40
std::list< std::string > parameters
Definition Utilities.h:46
std::string host
Definition Utilities.h:42
std::string interface
Definition Utilities.h:43
std::string instance
Definition Utilities.h:44
std::string protocol
Definition Utilities.h:45