ewmscp  ..
dirCount.cpp
Go to the documentation of this file.
1 #include <chrono>
2 #include <Options.h>
3 #include <scoped.h>
4 #include <throwcall.h>
5 #include <forward_list>
6 #include <fcntl.h>
7 #include <thread>
8 #include "timer.h"
17 #include <deque>
18 #include <map>
19 #include <set>
20 #include <mutex>
21 #include <condition_variable>
22 #include <stdexcept>
23 #include <thread>
24 #include <memory>
25 #include <iostream>
26 
27 namespace MyWaitQueues {
28  template <typename T> class simple {
29  private:
30  std::deque<std::unique_ptr<T>> queue;
31  std::mutex queue_mutex;
32  public:
33  void enqueue(std::unique_ptr<T>& item) {
34  std::unique_lock<decltype(queue_mutex)> lock(queue_mutex); //(NOLINT clang-analyzer-alpha.core.CastToStruct)
35  queue.emplace_back(std::move(item));
36  }
37  template <class ... Types> void emplace(Types ... args) {
38  std::unique_ptr<T> item(new T(args...));
39  enqueue(item);
40  }
41  template <class ... Types> std::unique_ptr<T> dequeue() {
42  std::unique_lock<decltype(queue_mutex)> lock(queue_mutex); //(NOLINT clang-analyzer-alpha.core.CastToStruct)
43  if (queue.empty()) {
44  return nullptr;
45  }
46  auto item = std::move(queue.front());
47  queue.pop_front();
48  return item;
49  }
50  decltype(queue.size()) size() const {
51  return queue.size();
52  }
53  decltype(queue.empty()) empty() const {
54  return queue.empty();
55  }
56  };
57 } // namespace MyWaitQueues
58 
59 
60 
61 
62 
63 static options::single<bool> ignoreAccess('i', "ignoreAccess",
64  "ignore subdirs we may not access");
65 static options::single<bool> quiet('q', "quiet",
66  "be quiet, no prefix to output lines");
67 static options::single<bool> reportUnknowns('U',"reportUnknowns",
68  "print a list of items with type DT_UNKNOWN");
69 
70 class counterType {
71  public:
72  enum specials : unsigned char {
73  denied = 253,
74  noent = 254,
75  notdir = 255
76  };
78  public:
79  const unsigned char index;
80  const std::string name;
81  const unsigned char optionChar;
83  itemDescriptor(unsigned char aIndex,
84  const std::string& aName,
85  unsigned char aOptionChar):
86  index(aIndex),
87  name(aName),
88  optionChar(aOptionChar) {
89  option = new options::single<bool>(aOptionChar, aName,
90  "print count of " + aName + " items");
91  }
92  };
93  private:
94  std::array<uint64_t, 256> counters;
95  static const std::vector<itemDescriptor>& keyToName() {
96  const static std::vector<itemDescriptor> kTN{
97  {DT_BLK, "blockDevice", 'b'},
98  {DT_CHR, "characterDevice", 'c'},
99  {DT_DIR, "directory", 'd'},
100  {DT_FIFO, "fifo", 'p'},
101  {DT_LNK, "symlink", 'l'},
102  {DT_REG, "file", 'f'},
103  {DT_SOCK, "socket", 's'},
104  {DT_UNKNOWN, "unknown", 'u'},
105  {specials::denied, "accesDenied", 'D'},
106  {specials::noent, "noSuchFile", 'M'},
107  {specials::notdir, "notADirectory", 'N'}
108  };
109  return kTN;
110  }
111  public:
113  static void init() {
114  keyToName();
115  }
116  void count(unsigned char what) {
117  counters[what]++;
118  }
119  void sum(counterType& other) {
120  for (const auto& item : keyToName()) {
121  other.counters[item.index] += counters[item.index];
122  }
123  }
124  void print() {
125  bool printAll(true);
126  for (const auto& item : keyToName()) {
127  if (*item.option) {
128  printAll = false;
129  }
130  }
131  for (const auto& item : keyToName()) {
132  if (printAll || *item.option) {
133  if (!quiet) {
134  std::cout << item.name << " ";
135  }
136  std::cout << counters[item.index] << "\n";
137  }
138  }
139  }
140 };
141 
143 static options::single<unsigned> nThreads('n', "nThreads", "number of threads", 0);
144 
145 static std::forward_list<std::string> unknownList;
146 static std::mutex unknownListMutex;
147 
148 class runnerType {
150  unsigned int dirsDone;
151  static std::mutex listMutex;
152  static std::condition_variable cv;
153  static std::atomic<unsigned int>nActiveWorkers;
154  public:
155  static std::forward_list<runnerType>& runners() {
156  static std::forward_list<runnerType> list;
157  return list;
158  }
159  static unsigned int nWorkers;
160  std::thread worker;
162  worker = std::thread(&runnerType::process, this, std::ref(dirQueue));
163  }
164  public:
165  static void newWorker(dirQueueType& dirQueue) {
166  std::unique_lock<decltype(listMutex)> lock(listMutex);
167  if (nWorkers < nThreads) {
168  nWorkers++;
169  nActiveWorkers++;
170  runners().emplace_front(dirQueue);
171  }
172  }
173  static void finish(counterType& sumCounter, bool verbose) {
174  std::unique_lock<decltype(listMutex)> lock(listMutex);
175  cv.wait(lock, [] {return nActiveWorkers == 0;});
176  for (auto& runner : runners()) {
177  runner.worker.join();
178  runner.sum(sumCounter);
179  }
180  if (verbose) {
181  unsigned int realizedThreads(0);
182  unsigned int unusedThreads(0);
183  for (auto& runner : runners()) {
184  realizedThreads++;
185  if (runner.getDirsDone() == 0) {
186  unusedThreads++;
187  }
188  }
189  std::cerr << "of " << realizedThreads << " threads " << unusedThreads << " were not used\n";
190  }
191  }
192 
193 
194 
195  void sum(counterType& other) {
196  counter.sum(other);
197  }
198  unsigned int getDirsDone() const {
199  return dirsDone;
200  }
201  void process(dirQueueType& dirQueue) {
202  while (auto path = dirQueue.dequeue()) {
203  dirsDone++;
204  static timer::anchor a("opendir");
206  auto result = opendir(path->c_str());
207  i.stop();
208  if (result == nullptr) {
209  if (errno == EACCES && ignoreAccess) {
210  counter.count(counterType::specials::denied);
211  continue;
212  } else if (errno == ENOENT) {
213  counter.count(counterType::specials::noent);
214  continue;
215  } else if (errno == ENOTDIR) {
216  counter.count(counterType::specials::notdir);
217  continue;
218  }
219  throwcall::badval(result, nullptr, "can't open directory ", *path);
220  }
221  scoped::Dir dir(*path, result);
222  static timer::anchor b("readdir");
224  while (auto entry = readdir(dir)) {
225  I.stop();
226  if (entry->d_name[entry->d_name[0] != '.' ? 0 : entry->d_name[1] != '.' ? 1 : 2] == '\0') {
227  I.restart();
228  continue; // skip . .. and empty strings
229  }
230  counter.count(entry->d_type);
231  if (entry->d_type == DT_DIR) {
232  std::string subdir(*path);
233  subdir += "/";
234  subdir += entry->d_name;
235  dirQueue.emplace(subdir);
236  newWorker(dirQueue);
237  } else if (entry->d_type == DT_UNKNOWN && reportUnknowns) {
238  std::string item(*path);
239  item += "/";
240  item += entry->d_name;
241  std::unique_lock<decltype(unknownListMutex)> lock(unknownListMutex); //(NOLINT clang-analyzer-alpha.core.CastToStruct)
242  unknownList.emplace_front(item);
243  }
244  I.restart();
245  }
246  }
247  std::unique_lock<decltype(listMutex)> lock;
248  --nActiveWorkers;
249  cv.notify_one();
250  }
251 };
252 
253 unsigned int runnerType::nWorkers(0);
254 std::mutex runnerType::listMutex;
255 std::condition_variable runnerType::cv;
256 std::atomic<unsigned int> runnerType::nActiveWorkers(0);
257 
258 
259 void handleDir(const std::string& path, counterType& counter) {
260  static timer::anchor a("opendir");
262  auto result = opendir(path.c_str());
263  i.stop();
264  if (result == nullptr) {
265  if (errno == EACCES && ignoreAccess) {
266  counter.count(counterType::specials::denied);
267  return;
268  } else if (errno == ENOENT) {
269  counter.count(counterType::specials::noent);
270  return;
271  } else if (errno == ENOTDIR) {
272  counter.count(counterType::specials::notdir);
273  return;
274  }
275  throwcall::badval(result, nullptr, "can't open directory ", path);
276  }
277  scoped::Dir dir(path, result);
278  std::forward_list<std::string> subdirs;
279  static timer::anchor b("readdir");
281  while (auto entry = readdir(dir)) {
282  I.stop();
283  if (entry->d_name[entry->d_name[0] != '.' ? 0 : entry->d_name[1] != '.' ? 1 : 2] == '\0') {
284  I.restart();
285  continue; // skip . .. and empty strings
286  }
287  counter.count(entry->d_type);
288  if (entry->d_type == DT_DIR) {
289  subdirs.emplace_front(entry->d_name);
290  } else if (entry->d_type == DT_UNKNOWN && reportUnknowns) {
291  std::string item(path);
292  item += "/";
293  item += entry->d_name;
294  unknownList.emplace_front(item);
295  }
296  I.restart();
297  }
298  for (const auto& subdir : subdirs) {
299  handleDir(path + "/" + subdir, counter);
300  }
301 
302 }
303 
304 int main(int argc, const char *argv[]) {
305  options::parser parser("dirCount", "", {});
307  "paths to consider");
308  options::single<bool> timing('t', "timing", "show timing info");
310  parser.fParse(argc, argv);
311 
312  counterType counter;
313  if (nThreads == 1) {
314  for (auto path : paths) {
315  handleDir(path, counter);
316  }
317  } else {
318  if (nThreads == 0) {
319  nThreads = sysconf(_SC_NPROCESSORS_ONLN);
320  }
321  dirQueueType dirQueue;
322  for (const auto& path : paths) {
323  dirQueue.emplace(path);
324  }
325  for (unsigned int i = 0; i < paths.size(); i++) {
326  runnerType::newWorker(dirQueue);
327  }
328  runnerType::finish(counter, timing);
329  }
330  counter.print();
331  if (timing) {
332  timer::anchor::print(std::cerr, "");
333  }
334  if (reportUnknowns) {
335  std::cout << "List of items of type DT_UNKNOWN:\n";
336  for (const auto& item: unknownList) {
337  std::cout << item << "\n";
338  }
339  }
340  return 0;
341 }
counterType::counterType
counterType()
Definition: dirCount.cpp:112
counterType::itemDescriptor::name
const std::string name
Definition: dirCount.cpp:80
timer::instanceUnscoped::restart
void restart()
Definition: timer.h:133
options::parser
class that contains the parser, i.e. does that option handling
Definition: Options.h:363
verbose
options::single< bool > verbose
options::single< unsigned >
runnerType::cv
static std::condition_variable cv
Definition: dirCount.cpp:152
dirQueueType
MyWaitQueues::simple< std::string > dirQueueType
Definition: dirCount.cpp:142
unknownList
static std::forward_list< std::string > unknownList
Definition: dirCount.cpp:145
counterType::keyToName
static const std::vector< itemDescriptor > & keyToName()
Definition: dirCount.cpp:95
options::single< bool >
class specialisation for options of type bool
Definition: Options.h:595
MyWaitQueues
Definition: dirCount.cpp:27
throwcall::badval
T badval(T call, t badvalue, const Args &... args)
template function to wrap system calls that return a special bad value on failure
Definition: throwcall.h:54
counterType::itemDescriptor::itemDescriptor
itemDescriptor(unsigned char aIndex, const std::string &aName, unsigned char aOptionChar)
Definition: dirCount.cpp:83
counterType::print
void print()
Definition: dirCount.cpp:124
counterType::counters
std::array< uint64_t, 256 > counters
Definition: dirCount.cpp:94
counterType::notdir
@ notdir
Definition: dirCount.cpp:75
handleDir
void handleDir(const std::string &path, counterType &counter)
Definition: dirCount.cpp:259
runnerType::getDirsDone
unsigned int getDirsDone() const
Definition: dirCount.cpp:198
counterType::count
void count(unsigned char what)
Definition: dirCount.cpp:116
runnerType::process
void process(dirQueueType &dirQueue)
Definition: dirCount.cpp:201
runnerType::newWorker
static void newWorker(dirQueueType &dirQueue)
Definition: dirCount.cpp:165
counterType::itemDescriptor::optionChar
const unsigned char optionChar
Definition: dirCount.cpp:81
MyWaitQueues::simple
Definition: dirCount.cpp:28
main
int main(int argc, const char *argv[])
Definition: dirCount.cpp:304
scoped.h
Options.h
runnerType::worker
std::thread worker
Definition: dirCount.cpp:160
runnerType::runners
static std::forward_list< runnerType > & runners()
Definition: dirCount.cpp:155
timer::anchor::print
static void print(std::ostream &out, const std::string &prefix)
Definition: timer.cpp:5
nThreads
static options::single< unsigned > nThreads('n', "nThreads", "number of threads", 0)
runnerType::nWorkers
static unsigned int nWorkers
Definition: dirCount.cpp:159
counterType::noent
@ noent
Definition: dirCount.cpp:74
timer::instanceUnscoped::stop
void stop()
Definition: timer.h:107
counterType::itemDescriptor::index
const unsigned char index
Definition: dirCount.cpp:79
timer.h
throwcall.h
MyWaitQueues::simple::queue
std::deque< std::unique_ptr< T > > queue
Definition: dirCount.cpp:30
timer::instanceUnscoped
Definition: timer.h:95
runnerType::dirsDone
unsigned int dirsDone
Definition: dirCount.cpp:150
counterType::specials
specials
Definition: dirCount.cpp:72
runnerType::runnerType
runnerType(dirQueueType &dirQueue)
Definition: dirCount.cpp:161
MyWaitQueues::simple::queue_mutex
std::mutex queue_mutex
Definition: dirCount.cpp:31
MyWaitQueues::simple::enqueue
void enqueue(std::unique_ptr< T > &item)
Definition: dirCount.cpp:33
timer::anchor
Definition: timer.h:22
MyWaitQueues::simple::emplace
void emplace(Types ... args)
Definition: dirCount.cpp:37
counterType::init
static void init()
Definition: dirCount.cpp:113
runnerType
Definition: dirCount.cpp:148
unknownListMutex
static std::mutex unknownListMutex
Definition: dirCount.cpp:146
MyWaitQueues::simple::size
decltype(queue.size()) size() const
Definition: dirCount.cpp:50
runnerType::counter
counterType counter
Definition: dirCount.cpp:149
runnerType::sum
void sum(counterType &other)
Definition: dirCount.cpp:195
ignoreAccess
static options::single< bool > ignoreAccess('i', "ignoreAccess", "ignore subdirs we may not access")
counterType::itemDescriptor
Definition: dirCount.cpp:77
MyWaitQueues::simple::empty
decltype(queue.empty()) empty() const
Definition: dirCount.cpp:53
scoped::Dir
Definition: scoped.h:71
options::positional
Definition: Options.h:876
runnerType::finish
static void finish(counterType &sumCounter, bool verbose)
Definition: dirCount.cpp:173
counterType::sum
void sum(counterType &other)
Definition: dirCount.cpp:119
quiet
static options::single< bool > quiet('q', "quiet", "be quiet, no prefix to output lines")
MyWaitQueues::simple::dequeue
std::unique_ptr< T > dequeue()
Definition: dirCount.cpp:41
reportUnknowns
static options::single< bool > reportUnknowns('U',"reportUnknowns", "print a list of items with type DT_UNKNOWN")
counterType
Definition: dirCount.cpp:70
runnerType::nActiveWorkers
static std::atomic< unsigned int > nActiveWorkers
Definition: dirCount.cpp:153
counterType::denied
@ denied
Definition: dirCount.cpp:73
counterType::itemDescriptor::option
options::single< bool > * option
Definition: dirCount.cpp:82
runnerType::listMutex
static std::mutex listMutex
Definition: dirCount.cpp:151