WifiStatistics.cc
Go to the documentation of this file.
1 //
2 // Copyright (c) 2020 Fraunhofer Institute for Applied Information Technology (FIT)
3 // Network Research Group (NET)
4 // Schloss Birlinghoven, 53754 Sankt Augustin, GERMANY
5 // Contact: support@wiback.org
6 //
7 // This file is part of the SENF code tree.
8 // It is licensed under the 3-clause BSD License (aka New BSD License).
9 // See LICENSE.txt in the top level directory for details or visit
10 // https://opensource.org/licenses/BSD-3-Clause
11 //
12 
13 
14 #include "WifiStatistics.hh"
15 
16 // Custom includes
17 #include <iostream>
18 #include <sstream>
19 #include <string>
20 #include <algorithm>
21 #include <sys/types.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <boost/filesystem.hpp>
26 #include <boost/filesystem/fstream.hpp>
27 #include <boost/property_tree/json_parser.hpp>
28 #include <boost/algorithm/string.hpp>
30 
31 #define prefix_
32 
34 
35 prefix_ senf::mmapFile::mmapFile(std::string const & fname)
36  : fd_(-1), begin_(NULL), end_(NULL), next_(NULL), buffer_(NULL)
37 {
38  if (!fname.empty()) {
39  if (!open(fname)) {
40  throw std::exception();
41  }
42  }
43 }
44 
46 {
47  if (fd_ != -1)
48  close(fd_);
49  if (buffer_)
50  delete buffer_;
51 }
52 
53 prefix_ void *senf::mmapFile::open(std::string const & fname)
54 {
55  fd_ = ::open(fname.c_str(), O_RDONLY);
56  if (fd_ < 0)
57  return NULL;
58 
59  if (fstat(fd_, &stat_) < 0) {
60  close (fd_); fd_ = -1;
61  return NULL;
62  }
63 
64  begin_ = (std::uint8_t *) mmap(0, stat_.st_size, PROT_READ, MAP_SHARED, fd_, 0);
65  if (begin_ == MAP_FAILED) {
66  // mmap failed, fall back to a convential read()
67  size_t bsize (stat_.st_size > 0 ? stat_.st_size : 32768);
68  buffer_ = new std::uint8_t[bsize];
69  if ((bsize = ::read(fd_, buffer_, bsize)) <= 0) {
70  close (fd_); fd_ = -1;
71  delete buffer_; buffer_ = NULL;
72  return NULL;
73  }
74  begin_ = buffer_;
75  end_ = begin_ + bsize;
76  } else {
77  end_ = begin_ + stat_.st_size;
78  }
79 
80  next_ = begin_;
81  return begin_;
82 }
83 
85  const
86 {
87  return begin_;
88 }
89 
91  const
92 {
93  return end_;
94 }
95 
97  const
98 {
99  return next_ >= end_;
100 }
101 
102 
104  const
105 {
106  return off_t(end_ - begin_);
107 }
108 
110  : signal(StatisticAccumulator<std::int64_t>(wsk.signal.sum, wsk.signal.sum2, wsk.signal.min, wsk.signal.max, wsk.signal.count).data()),
112  bitrate(StatisticAccumulator<std::int64_t>(wsk.bitrate.sum, wsk.bitrate.sum2, wsk.bitrate.min, wsk.bitrate.max, wsk.bitrate.count).data()),
114  total(wsk.pktCounts.rx_packets),
115  totalBytes(wsk.pktCounts.rx_bytes),
116  badFCS(wsk.pktCounts.bad_fcs_packets),
117  badFCSBytes(wsk.pktCounts.bad_fcs_bytes),
118  rTx(wsk.pktCounts.rTx_packets),
119  rTxBytes(wsk.pktCounts.rTx_bytes),
120  airTime(wsk.pktCounts.air_time),
121  lastSeen(senf::ClockService::milliseconds(wsk.lastSeen)),
122  bssId(senf::MACAddress::from_data(wsk.bssid)),
123  ssId(wsk.ssid),
125 {
126 }
127 
128 prefix_ senf::emu::WifiStatistics::WifiStatistics(std::string ifName, std::string debugFS)
129  : timestamp_ (senf::ClockService::clock_type(0)), tag_(0),
130  invalidEntries_(0), ioErrors_(0)
131 {
132  debugFsPath_ = debugFS + "/wifi_statistics/" + ifName + "/";
133 }
134 
135 
137 {
138  enable(false);
139 }
140 
142 {
143  std::string file (debugFsPath_ + "active");
144  int fd;
145  if ((fd = open( file.c_str(), O_WRONLY)) != -1) {
146  senf::IGNORE(write( fd, on ? "1" : "0", 1));
147  close( fd);
148  return true;
149  }
150 
151  return false;
152 }
153 
155 {
156  if (timestamp_ and (tag_ == tag))
157  return true;
158 
159  map_.clear();
160  timestamp_ = senf::ClockService::clock_type(0);
161 
162  try {
163  boost::property_tree::ptree pt;
164  boost::property_tree::read_json(debugFsPath_ + "stats", pt);
165 
166  for (auto const & v : pt) {
168  unsigned num (0); // keep track of numer of parsed items (we need 8)
169  for (auto const & it : v.second) {
170  if (it.first == "signal") {
171  std::int32_t sum(0), min(0), max(0);
172  std::uint32_t count(0);
173  std::uint64_t sum2 (0);
174  for (auto const & s : it.second) {
175  if (s.first == "sum")
176  sum = std::stoi(s.second.data());
177  if (s.first == "sum2")
178  sum2 = std::stoull(s.second.data());
179  if (s.first == "min")
180  min = std::stoi(s.second.data());
181  if (s.first == "max")
182  max = std::stoi(s.second.data());
183  if (s.first == "count")
184  count = std::stoul(s.second.data());
185  }
187  num++;
188  }
189  else if (it.first == "signalNonData") {
190  // optional
191  std::int32_t sum(0), min(0), max(0);
192  std::uint32_t count(0);
193  std::uint64_t sum2 (0);
194  for (auto const & s : it.second) {
195  if (s.first == "sum")
196  sum = std::stoi(s.second.data());
197  if (s.first == "sum2")
198  sum2 = std::stoull(s.second.data());
199  if (s.first == "min")
200  min = std::stoi(s.second.data());
201  if (s.first == "max")
202  max = std::stoi(s.second.data());
203  if (s.first == "count")
204  count = std::stoul(s.second.data());
205  }
207  }
208  else if (it.first == "bitrate") {
209  // optional
210  std::uint32_t sum(0), min(0), max(0), count(0);
211  std::uint64_t sum2 (0);
212  for (auto const & s : it.second) {
213  if (s.first == "sum")
214  sum = std::stoul(s.second.data());
215  if (s.first == "sum2")
216  sum2 = std::stoull(s.second.data());
217  if (s.first == "min")
218  min = std::stoul(s.second.data());
219  if (s.first == "max")
220  max = std::stoul(s.second.data());
221  if (s.first == "count")
222  count = std::stoul(s.second.data());
223  }
225  }
226  else if (it.first == "bitrateNonData") {
227  // optional
228  std::uint32_t sum(0), min(0), max(0), count(0);
229  std::uint64_t sum2 (0);
230  for (auto const & s : it.second) {
231  if (s.first == "sum")
232  sum = std::stoul(s.second.data());
233  if (s.first == "sum2")
234  sum2 = std::stoull(s.second.data());
235  if (s.first == "min")
236  min = std::stoul(s.second.data());
237  if (s.first == "max")
238  max = std::stoul(s.second.data());
239  if (s.first == "count")
240  count = std::stoul(s.second.data());
241  }
243  }
244  else if (it.first == "badFCS") {
245  data.badFCS = std::stoul(it.second.data());
246  num++;
247  }
248  else if (it.first == "badFCSBytes") {
249  data.badFCSBytes = std::stoul(it.second.data());
250  num++;
251  }
252  else if (it.first == "rTx") {
253  data.rTx = std::stoul(it.second.data());
254  num++;
255  }
256  else if (it.first == "rTxBytes") {
257  data.rTxBytes = std::stoul(it.second.data());
258  num++;
259  }
260  else if (it.first == "total") {
261  data.total = std::stoul(it.second.data());
262  num++;
263  }
264  else if (it.first == "totalBytes") {
265  data.totalBytes = std::stoul(it.second.data());
266  num++;
267  }
268  else if (it.first == "airTime") {
269  data.airTime = std::stoul(it.second.data());
270  num++;
271  }
272  else if (it.first == "bssId") {
273  // optional
274  data.bssId = senf::MACAddress::from_string(it.second.data());
275  }
276  else if (it.first == "ssId") {
277  // optional
278  data.ssId = it.second.data();
279  }
280  else if (it.first == "type") {
281  // optional
282  data.type = WifiStatisticsData::Type(std::stoul(it.second.data()));
283  }
284  else if (it.first == "lastSeen") {
285  // optional
286  data.lastSeen = senf::ClockService::milliseconds(std::stoul(it.second.data()));
287  }
288  }
289  if (num == 8) {
290  if (data.totalBytes > 0 and data.lastSeen <= maxAge) {
291  // only add entries with activity
292  map_.emplace(std::make_pair(senf::MACAddress::from_string(v.first), data));
293  }
294  } else {
295  invalidEntries_++;
296  }
297  }
298  } catch(std::exception const & ex) {
299  ioErrors_++;
300  map_.clear();
301  return false;
302  }
303 
304  tag_ = tag;
305  timestamp_ = senf::scheduler::now();
306 
307  return true;
308 }
309 
310 static void fast_split(char *str, char sep, std::vector<char *> & tokens)
311 {
312  unsigned int start = 0, stop;
313  for (stop = 0; str[stop]; stop++) {
314  if (str[stop] == sep) {
315  str[stop] = '\0';
316  tokens.emplace_back(str + start);
317  start = stop + 1;
318  } else if (str[stop] == '\n') {
319  str[stop] = '\0';
320  }
321  }
322  tokens.emplace_back(str + start);
323 }
324 
325 static inline std::uint32_t atou(const char * str)
326 {
327  std::uint32_t x (0);
328  while (*str != '\0') {
329  x = (x*10) + (*str - '0');
330  ++str;
331  }
332  return x;
333 }
334 
335 static inline std::uint64_t atoull(const char * str)
336 {
337  return strtoull(str, NULL, 10);
338 }
339 
341 {
342  if (timestamp_ and (tag_ == tag))
343  return true;
344 
345  map_.clear();
346  timestamp_ = senf::ClockService::clock_type(0);
347 
348  try {
349  FILE * statsFile;
350  char dataLine[512];
351  if ((statsFile = fopen((debugFsPath_ + "stats_csv").c_str(), "r")) != NULL) {
352  while (fgets(dataLine, sizeof(dataLine) -1, statsFile)) {
353  std::vector<char *> tokens;
354  fast_split(dataLine, ',', tokens);
355  if (tokens.size() == 32) {
357  // Format: see wifi-statistics/station.c
358  data.signal = StatisticAccumulator<std::int64_t>(atoi(tokens[1]), atoull(tokens[2]),
359  atoi(tokens[3]), atoi(tokens[4]),
360  atou(tokens[5])).data();
361  data.signalNonData = StatisticAccumulator<std::int64_t>(atoi(tokens[6]), atoull(tokens[7]),
362  atoi(tokens[8]), atoi(tokens[9]),
363  atou(tokens[10])).data();
364  data.bitrate = StatisticAccumulator<std::uint64_t>(atou(tokens[11]), atoull(tokens[12]),
365  atou(tokens[13]), atou(tokens[14]),
366  atou(tokens[15])).data();
367  data.bitrateNonData = StatisticAccumulator<std::uint64_t>(atou(tokens[16]), atoull(tokens[17]),
368  atou(tokens[18]), atou(tokens[19]),
369  atou(tokens[20])).data();
370 
371  data.badFCS = atou(tokens[21]);
372  data.badFCSBytes = atou(tokens[22]);
373  data.rTx = atou(tokens[23]);
374  data.rTxBytes = atou(tokens[24]);
375  data.total = atou(tokens[25]);
376  data.totalBytes = atou(tokens[26]);
377  data.airTime = atou(tokens[27]);
378  data.lastSeen = senf::ClockService::milliseconds(atou(tokens[28]));
379  data.bssId = senf::MACAddress::from_string(tokens[29]);
380  data.type = WifiStatisticsData::Type(atou(tokens[30]));
381  data.ssId = tokens[31];
382  if (data.totalBytes > 0 and data.lastSeen <= maxAge) {
383  // only add entries with activity
384  map_.emplace(std::make_pair(senf::MACAddress::from_string(tokens[0]), data));
385  }
386  } else {
387  invalidEntries_++;
388  }
389  }
390  fclose(statsFile);
391  } else {
392  ioErrors_++;
393  map_.clear();
394  return false;
395  }
396  } catch (...) {
397  ioErrors_++;
398  map_.clear();
399  return false;
400  };
401 
402  tag_ = tag;
403  timestamp_ = senf::scheduler::now();
404 
405  return true;
406 }
407 
409 {
410  if (timestamp_ and (tag_ == tag))
411  return true;
412 
413  map_.clear();
414  timestamp_ = senf::ClockService::clock_type(0);
415 
416  try {
417  mmapFile stats(debugFsPath_ + "stats_bin");
418  WifiStatsKernel *wsk ((WifiStatsKernel*) stats.begin());
419  unsigned n (0), num (stats.size() / sizeof(WifiStatsKernel));
420  while(n < num) {
421  if (wsk[n].pktCounts.rx_packets > 0 and wsk[n].lastSeen <= senf::ClockService::in_milliseconds(maxAge)) {
422  // only add entries with activity
423  map_.emplace(std::make_pair(senf::MACAddress::from_data(wsk[n].mac), WifiStatisticsData (wsk[n])));
424  }
425  n++;
426  }
427  } catch (...) {
428  ioErrors_++;
429  map_.clear();
430  return false;
431  };
432 
433  tag_ = tag;
434  timestamp_ = senf::scheduler::now();
435 
436  return true;
437 }
438 
439 
441  const
442 {
443  return map_;
444 }
445 
447  const
448 {
449  return timestamp_;
450 }
451 
453  const
454 {
455  return tag_;
456 }
457 
459  const
460 {
461  return invalidEntries_;
462 }
463 
465  const
466 {
467  return ioErrors_;
468 }
469 
471 {
472  pollStatisticsBIN(tag, maxAge);
473  return map_;
474 }
475 
477 #undef prefix_
config::time_type clock_type
#define prefix_
senf::StatisticsData bitrate
std::uint32_t rx_bytes
std::uint8_t mac[6]
static SENF_CLOCKSERVICE_CONSTEXPR int64_type in_milliseconds(clock_type const &v)
std::string str(T const &t)
u8 type
mmapFile(std::string const &fname)
senf::StatisticsData signal
StatsDataPktCountsKernel pktCounts
u8 data[SPECTRAL_HT20_NUM_BINS]
STL namespace.
StatsDataCollectorKernel bitrate
static MACAddress from_string(std::string const &s)
char ssid[36]
bool pollStatisticsBIN(std::uint32_t tag, senf::ClockService::clock_type const &maxAge)
bool enable(bool on=true)
std::int32_t min
std::uint32_t lastSeen
std::int32_t max
std::uint32_t invalidEntries() const
std::uint32_t rx_packets
std::uint32_t rTx_packets
WifiStatistics(std::string ifName, std::string debugFS="/sys/kernel/debug")
bool pollStatistics(std::uint32_t tag, senf::ClockService::clock_type const &maxAge)
std::uint32_t air_time
std::uint32_t rTx_bytes
ClockService::clock_type const & timestamp() const
StatsDataCollectorKernel signal
senf::StatisticsData signalNonData
static SENF_CLOCKSERVICE_CONSTEXPR clock_type milliseconds(int64_type const &v)
std::uint64_t sum2
WifiStatisticsMap const & map() const
std::uint32_t bad_fcs_bytes
std::uint8_t bssid[6]
bool eof() const
void * begin() const
bool pollStatisticsCSV(std::uint32_t tag, senf::ClockService::clock_type const &maxAge)
off_t size() const
boost::unordered_map< senf::MACAddress, WifiStatisticsData > WifiStatisticsMap
StatsDataCollectorKernel signalNonData
std::uint32_t count
StatsDataCollectorKernel bitrateNonData
std::uint32_t ioErrors() const
WifiStatisticsMap const & statisticsMap(std::uint32_t tag, senf::ClockService::clock_type const &maxAge)
void * open(std::string const &fname)
senf::ClockService::clock_type lastSeen
std::uint32_t bad_fcs_packets
static MACAddress from_data(InputIterator i)
senf::StatisticsData bitrateNonData
std::int32_t sum
void * end() const
virtual void close()
std::uint32_t tag() const