File: util.h

package info (click to toggle)
stenographer 0.0~git20180422.0.73ce5dd-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 19,112 kB
  • sloc: cpp: 2,062; sh: 782; yacc: 257; makefile: 102
file content (373 lines) | stat: -rw-r--r-- 11,435 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef STENOGRAPHER_UTIL_H_
#define STENOGRAPHER_UTIL_H_

// Contains small helper functions and types used throughout stenographer code.

#include <stdint.h>
#include <stdio.h>
#include <time.h>      // strftime(), gmtime(), time(),
#include <sys/time.h>  // gettimeofday()
#include <pthread.h>   // pthread_self()
#include <stdlib.h>
#include <string.h>
#include <execinfo.h>  // backtrace(), backtrace_symbols()

#include <condition_variable>
#include <deque>
#include <iomanip>   // setw, setfill
#include <iostream>  // cerr
#include <memory>
#include <mutex>
#include <sstream>  // stringstream
#include <string>   // string
#include <thread>

namespace {

struct timespec clock_realtime, clock_monotonic;
clockid_t clock_mono_id = CLOCK_MONOTONIC;
bool InitTime() {
  clock_gettime(CLOCK_REALTIME, &clock_realtime);
#ifdef CLOCK_MONOTONIC_RAW
  // If monotinic raw clock is supported and available, let's use that.
  if (!clock_gettime(CLOCK_MONOTONIC_RAW, &clock_monotonic)) {
    clock_mono_id = CLOCK_MONOTONIC_RAW;
    return true;
  }
#endif
  clock_gettime(CLOCK_MONOTONIC, &clock_monotonic);
  return true;
}
bool run_init_time = InitTime();

}  // namespace

namespace st {

#define DISALLOW_COPY_AND_ASSIGN(name) \
  name(const name&);                   \
  name(const name&&);                  \
  void operator=(const name&)

const int64_t kNumNanosPerMicro = 1000;
const int64_t kNumMicrosPerMilli = 1000;
const int64_t kNumMillisPerSecond = 1000;
const int64_t kNumNanosPerMilli = kNumNanosPerMicro * kNumMicrosPerMilli;
const int64_t kNumMicrosPerSecond = kNumMicrosPerMilli * kNumMillisPerSecond;
const int64_t kNumNanosPerSecond =
    kNumMillisPerSecond * kNumMicrosPerMilli * kNumNanosPerMicro;

inline int64_t GetCurrentTimeNanos() {
  struct timespec tv;
  clock_gettime(clock_mono_id, &tv);
  int64_t secs = clock_realtime.tv_sec - clock_monotonic.tv_sec + tv.tv_sec;
  int64_t nsecs = clock_realtime.tv_nsec - clock_monotonic.tv_nsec + tv.tv_nsec;
  return secs * 1000000000 + nsecs;
}
inline int64_t GetCurrentTimeMicros() { return GetCurrentTimeNanos() / 1000; }
inline void SleepForNanoseconds(int64_t nanos) {
  if (nanos <= 0) {
    return;
  }
  struct timespec tv;
  tv.tv_sec = nanos / kNumNanosPerSecond;
  tv.tv_nsec = nanos % kNumNanosPerSecond;
  while (EINTR == clock_nanosleep(CLOCK_MONOTONIC, 0, &tv, &tv)) {
  }
}
inline void SleepForMicroseconds(int64_t micros) {
  SleepForNanoseconds(micros * kNumNanosPerMicro);
}
inline void SleepForSeconds(double seconds) {
  SleepForNanoseconds(seconds * kNumNanosPerSecond);
}

////////////////////////////////////////////////////////////////////////////////
//// Logging helpers
//
// Simple methods for logging intersting events.
// LOG(INFO) << "blah";  // normal logging message
// LOG(FATAL) << "blah";  // log, then crash the program.  something is wrong.
// CHECK(b) << "blah";  // if 'b' is false, crash with given message.

namespace {

const size_t kTimeBufferSize = 20;
const char* kTimeFormat = "%Y-%m-%dT%H:%M:%S";

}  // namespace

class LogLine {
 public:
  LogLine(bool crash, const char* file, int line) : crash_(crash) {
    FillTimeBuffer();
    uint32_t tid = uint32_t(pthread_self()) >> 8;  // first bits always 0.
    ss_ << std::setfill('0') << time_buffer_ << "." << std::setw(6)
        << tv_.tv_usec << "Z T:" << std::hex << std::setw(6) << tid
        << std::setw(0) << std::dec << " [" << file << ":" << line << "] ";
  }
  ~LogLine() {
    ss_ << "\n";
    std::cerr << ss_.str() << std::flush;
    if (crash_) {
      std::cerr << "ABORTABORTABORT" << std::endl;
      void* backtraces[32];
      int size = backtrace(backtraces, 32);
      char** symbols = backtrace_symbols(backtraces, size);
      for (int i = 0; i < size; i++) {
        std::cerr << symbols[i] << std::endl;
      }
      free(symbols);
      abort();
    }
  }

  template <class T>
  LogLine& operator<<(const T& data) {
    ss_ << data;
    return *this;
  }

 private:
  void FillTimeBuffer() {
    gettimeofday(&tv_, NULL);
    struct tm timeinfo;
    gmtime_r(&tv_.tv_sec, &timeinfo);
    size_t len =
        strftime(time_buffer_, kTimeBufferSize, kTimeFormat, &timeinfo);
    if (len + 1 != kTimeBufferSize) {  // returned num bytes doesn't include \0
      strcpy(time_buffer_, "STRFTIME_ERROR");
    }
  }

  std::stringstream ss_;
  struct timeval tv_;
  char time_buffer_[kTimeBufferSize];
  bool crash_;

  DISALLOW_COPY_AND_ASSIGN(LogLine);
};

extern int logging_verbose_level;

typedef std::unique_ptr<std::string> Error;

}  // namespace st
// Define macros OUTSIDE of the 'st' namespace.

#define LOGGING_FATAL_CRASH true
#define LOGGING_ERROR_CRASH false
#define LOGGING_INFO_CRASH false

#define LOGGING_FATAL_LOG true
#define LOGGING_ERROR_LOG true
#define LOGGING_INFO_LOG (logging_verbose_level > 0)

#ifndef VLOG
#define VLOG(x) if (logging_verbose_level > (x)) \
  ::st::LogLine(false, __FILE__, __LINE__)
#endif
#ifndef LOG
#define LOG(level)           \
  if (LOGGING_##level##_LOG) \
  ::st::LogLine(LOGGING_##level##_CRASH, __FILE__, __LINE__)
#endif
#ifndef CHECK
#define CHECK(expr) \
  if (!(expr)) LOG(FATAL) << "CHECK(" #expr ") "
#endif

#define SUCCEEDED(x) ((x).get() == NULL)
#define SUCCESS NULL

#define ERROR(x) ::st::Error(new std::string(x))

#define RETURN_IF_ERROR(status, msg)                   \
  do {                                                 \
    ::st::Error __return_if_error_status__ = (status); \
    if (!SUCCEEDED(__return_if_error_status__)) {      \
      __return_if_error_status__->append(" <- ");      \
      __return_if_error_status__->append(msg);         \
      return __return_if_error_status__;               \
    }                                                  \
  } while (false)

#define LOG_IF_ERROR(status, msg)                            \
  do {                                                       \
    ::st::Error __log_if_error_status__ = (status);          \
    if (!SUCCEEDED(__log_if_error_status__)) {               \
      LOG(ERROR) << msg << ": " << *__log_if_error_status__; \
    }                                                        \
  } while (false)

#define CHECK_SUCCESS(x)                                                   \
  do {                                                                     \
    ::st::Error __check_success_error__ = (x);                             \
    CHECK(SUCCEEDED(__check_success_error__)) << #x << ": "                \
                                              << *__check_success_error__; \
  } while (false)

#define REPLACE_IF_ERROR(initial, replacement)         \
  do {                                                 \
    ::st::Error __replacement_error__ = (replacement); \
    if (!SUCCEEDED(__replacement_error__)) {           \
      if (!SUCCEEDED(initial)) {                       \
        LOG(ERROR) << "replacing error: " << *initial; \
      }                                                \
      initial = std::move(__replacement_error__);      \
    }                                                  \
  } while (false)

namespace st {

////////////////////////////////////////////////////////////////////////////////
//// Synchronization primitives
//
// The very simple Mutex class, and its RAII locker Mutex::Locker provide very
// simple blocking locks/unlocks wrapping a pthreads mutex.

// Barrier provides a barrier for multiple threads.
class Barrier {
 public:
  explicit Barrier(int threads) : threads_(threads), count_(0) {}
  ~Barrier() {}

  // The first N-1 calls to Block block until the Nth call.
  void Block();

 private:
  int threads_;
  int count_;
  std::mutex mu_;
  std::condition_variable cond_;

  DISALLOW_COPY_AND_ASSIGN(Barrier);
};

// Notification allows multiple threads to wait for one thread to do something.
class Notification {
 public:
  Notification() : waiting_(true) {}
  ~Notification() {}

  // Block until Notify is called.
  void WaitForNotification();

  // Unblock WaitForNotification calls.
  void Notify();

 private:
  bool waiting_;
  std::mutex mu_;
  std::condition_variable cond_;

  DISALLOW_COPY_AND_ASSIGN(Notification);
};

// ProducerConsumerQueue is a very simple thread-safe FIFO queue.
class ProducerConsumerQueue {
 public:
  ProducerConsumerQueue() : closed_(false) {}
  ~ProducerConsumerQueue() {}

  // Add value onto the queue.  Must not be NULL.
  void Put(void* val);

  // Get value off of the queue.  Gets NULL if queue is closed.
  void* Get();

  // Close queue.  All subsequent Get calls will immediately return NULL.
  void Close();

 private:
  std::mutex mu_;
  std::condition_variable cond_;
  bool closed_;
  std::deque<void*> d_;
  DISALLOW_COPY_AND_ASSIGN(ProducerConsumerQueue);
};

// Errno returns an Error based on the current value of errno and the
// return value of a standard syscall.  If ret is >= 0, returns OK.
// Otherwise, returns an error based on errno.
inline Error Errno(int ret = -1) {
  if (ret >= 0 || errno == 0) {
    return SUCCESS;
  }
  return ERROR(strerror(errno));
}

// Linux libaio and libseccomp use a different errno convention than normal
// syscalls.  Instead of returning -1 and setting errno, they return -errno
// on errors.  This function takes in that return value and returns OK if
// ret >= 0 or an Errno error based on the negative return value.
inline Error NegErrno(int ret) {
  if (ret < 0) {
    return ERROR(strerror(-ret));
  }
  return SUCCESS;
}

////////////////////////////////////////////////////////////////////////////////
//// Filesystem helpers.

std::string Basename(const std::string& filename);
std::string Dirname(const std::string& filename);
inline std::string HiddenFile(const std::string& dirname, int64_t micros) {
  CHECK(dirname[dirname.size() - 1] == '/');
  return dirname + "." + std::to_string(micros);
}
inline std::string UnhiddenFile(const std::string& dirname, int64_t micros) {
  CHECK(dirname[dirname.size() - 1] == '/');
  return dirname + std::to_string(micros);
}

// Watchdog is a simple thread which causes a process crash if certain code
// paths aren't hit on a regular basis.
//
// Usage:
//   Watchdog dog("description", 5 /*seconds*/);
//   while (true) {
//     dostuff();
//     dog.Feed();  // If not called every 5 seconds, crashes.
//   }
//   // dog falls out of scope, its thread is canceled and it quietly goes away.
class Watchdog {
 public:
  // Create a watchdog that crashes if it hasn't been fed after X seconds.
  // If 'seconds' is <= zero, does nothing.
  Watchdog(std::string description, int seconds);

  // Constructor stops the watchdog.
  ~Watchdog();

  // Feed the watchdog.
  void Feed();

 private:
  void Watch();

  std::thread* t_;
  std::string description_;
  int seconds_;
  uint64_t ctr_;
  bool done_;
};

}  // namespace st

#endif  // STENOGRAPHER_UTIL_H_