artdaq_ganglia_plugin  v1_02_13
malloc_free.cc
1 // This file (malloc_free.cc) was created by Ron Rechenmacher <ron@fnal.gov> on
2 // Dec 2, 2015. "TERMS AND CONDITIONS" governing this file are in the README
3 // or COPYING file. If you do not have such a file, one can be obtained by
4 // contacting Ron or Fermi Lab in Batavia IL, 60510, phone: 630-840-3000.
5 // $RCSfile: malloc_free.cc,v $
6 static char const* rev = "$Revision: 1.12 $$Date: 2016/11/04 13:57:24 $";
7 
8 // This programm attemps to do a bunch of random mallocs and frees.
9 //
10 // make malloc_free CXX="g++ -std=c++0x"
11 // CXX="g++ -std=c++0x" make -e malloc_free # NOTE "-e"
12 
13 #include <stdio.h> // printf
14 #include <getopt.h> // getopt_long, {no,required,optional}_argument, extern char *optarg; extern int opt{ind,err,opt}
15 #include <stdlib.h> // malloc, rand - range [0, RAND_MAX] period at least 2**32
16 // random: 0 to RAND_MAX -- period 16 * ((2^31) - 1), has initstate() and setstate() functions
17 #include <libgen.h> // basename
18 #include <time.h> // time()
19 #include <sys/time.h> // gettimeofday
20 #include <unistd.h> // getpid()
21 #include <stdint.h> // uint64_t
22 #include <string.h> // memset
23 #include <vector>
24 #include <tuple> // pair
25 #include <set>
26 #include <mutex>
27 #include <boost/thread.hpp>
28 #include "send_gmetric.h"
29 
30 #define TRACE_NAME "malloc_free"
31 #if 1
32 # include "trace.h" // TRACE
33 #else
34 # define TRACE( vv, ... ) do { if (opt_v>=vv) printf( __VA_ARGS__ ); } while (0)
35 #endif
36 
37 #define USAGE "\
38  usage: %s [-hv?]\n\
39 \n\
40 example: %s\n\
41 \n\
42 option:\n\
43  -V print version and exit\n\
44  --help\n\
45  --seed= random number generator seed\n\
46  --min= min alloc default=%u\n\
47  --max= max alloc default=%u\n\
48  -t --time= seconds\n\
49  --new use new/delete\n\
50  --no-seed \n\
51  --ave= default=%d\n\
52  --threads= default=%u\n\
53 ", basename(argv[0]), basename(argv[0]), opt_min_alloc, opt_max_alloc\
54  , opt_ave, opt_threads
55 
56 /* GLOBALS */
57 uint64_t opt_hi_water = 1 * (1024 * 1024 * 1024);
58 uint64_t opt_lo_water = 8 * (1024 * 1024);
59 unsigned opt_seed = 2, opt_seed_ = 1; // 2 variables -- to allow any possible seed value
60 unsigned opt_max_alloc = 128 * (1024);
61 unsigned opt_min_alloc = 1 * (1024);
62 int opt_v = 0;
63 int opt_new = 0;
64 unsigned opt_ave = 100000;
65 int opt_threads = 1;
66 int opt_time = 25;
67 int g_stop = 0;
68 
69 uint64_t human(char* optarg)
70 {
71  char* endptr;
72  uint64_t ret = strtoul(optarg, &endptr, 0);
73  if (*endptr == 'k' || *endptr == 'K') ret *= 1024;
74  else if (*endptr == 'm' || *endptr == 'M') ret *= 1024 * 1024;
75  else if (*endptr == 'g' || *endptr == 'G') ret *= 1024 * 1024 * 1024;
76  return ret;
77 }
78 
79 void parse_args(int argc, char* argv[])
80 {
81  // parse opt, optargs, and args
82  while (1)
83  {
84  int opt;
85  static struct option long_options[] = {
86  // name has_arg *flag val
87  {"help", no_argument, 0, 'h'},
88  {"seed", required_argument,0, 's'},
89  {"hi", required_argument,0, 1},
90  {"lo", required_argument,0, 2},
91  {"min", required_argument,0, 3},
92  {"max", required_argument,0, 4},
93  {"no-seed", no_argument, 0, 5},
94  {"ave", required_argument,0, 6},
95  {"threads", required_argument,0, 7},
96  {"time", required_argument,0, 't'},
97  {0, 0, 0, 0}
98  };
99  opt = getopt_long(argc, argv, "?hvVs:t:",
100  long_options, NULL);
101  if (opt == -1) break;
102  switch (opt)
103  {
104  case '?': case 'h': printf(USAGE);
105  exit(0);
106  break;
107  case 'V': printf("%s\n", rev);
108  exit(0);
109  break;
110  case 'v': ++opt_v;
111  break;
112  case 's': opt_seed = strtoul(optarg, NULL, 0);
113  opt_seed_ = 1;
114  break;
115  case 't': opt_time = strtoul(optarg, NULL, 0);
116  break;
117  case 1: opt_hi_water = human(optarg);
118  break;
119  case 2: opt_lo_water = human(optarg);
120  break;
121  case 3: opt_min_alloc = human(optarg);
122  break;
123  case 4: opt_max_alloc = human(optarg);
124  break;
125  case 5: opt_seed_ = 0;
126  break;
127  case 6: opt_ave = strtoul(optarg, NULL, 0);
128  break;
129  case 7: opt_threads = strtoul(optarg, NULL, 0);
130  break;
131  default:
132  printf("?? getopt returned character code 0%o ??\n", opt);
133  exit(1);
134  }
135  }
136 }
137 
138 uint64_t gettimeofday_us(void)
139 {
140  struct timeval tv;
141  gettimeofday(&tv, NULL);
142  return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
143 }
144 
145 struct Stats
146 {
147  Stats() : count_(0)
148  , running_tot_us_(0)
149  , running_tot_MB_(0.0) {}
150 
151  void update(double MB, int64_t delta_us)
152  {
153  mtx_.lock();
154  running_tot_MB_ += MB;
155  running_tot_us_ += delta_us;
156  times_per_.insert((double)delta_us / MB);
157  ++count_;
158  mtx_.unlock();
159  }
160 
161  void get_clear(double& tot_MB, int64_t& tot_delta_us, int& count, double& min, double& max)
162  {
163  mtx_.lock();
164  tot_MB = running_tot_MB_;
165  tot_delta_us = running_tot_us_;
166  count = count_;
167  if (count == 0)
168  min = max = 0;
169  else
170  {
171  min = *(times_per_.begin());
172  max = *(times_per_.rbegin());
173  }
174  running_tot_MB_ = 0.0;
175  running_tot_us_ = 0;
176  times_per_.clear();
177  count_ = 0;
178  mtx_.unlock();
179  }
180 
181 private:
182  int count_;
183  int64_t running_tot_us_;
184  double running_tot_MB_;
185  std::multiset<double> times_per_;
186  std::mutex mtx_;
187 };
188 
189 struct Malloc
190 {
191  Malloc(Stats& stats) : num_mallocs(0)
192  , stats_(stats) {}
193 
194 #define NUM_DELTAS opt_ave
195  int num_mallocs;
196  Stats& stats_;
197 
198  // alloc_bytes is an output -- This function returns a ptr to the memory
199  // and the num bytes allocated.
200  void* malloc_opt(unsigned* alloc_bytes)
201  {
202  void* ptr;
203  if ((opt_max_alloc - opt_min_alloc) == 0)
204  *alloc_bytes = opt_max_alloc;
205  else
206  *alloc_bytes = (random() % (opt_max_alloc - opt_min_alloc)) + opt_min_alloc;
207 
208  uint64_t t0 = gettimeofday_us();
209 
210  ptr = malloc(*alloc_bytes);
211  if (ptr == NULL)
212  {
213  perror("malloc");
214  exit(1);
215  }
216  unsigned ii;
217  for (ii = 0; ii < *alloc_bytes; ii += 4096)
218  *(int*)((char*)ptr + ii) = 1;
219  ii /= 4096; // convert offset to pages
220 
221  int64_t delta_us = gettimeofday_us() - t0;
222 # define PAGES_PER_MB (0x100000/4096.0)
223  double MB = (double)ii / PAGES_PER_MB;
224  TLOG(TLVL_INFO) << "malloc " << *alloc_bytes << " bytes ii=" << ii << " MB=" << MB;
225 
226  stats_.update(MB, delta_us);
227 
228  return ptr;
229  }
230 };
231 
232 
233 static void do_work(Stats* stats)
234 {
235  std::vector<std::pair<void*, int>> ptr_vec;
236  uint64_t total_allocation = 0;
237  int direction; // 1 is up, 0 is down.
238  enum
239  {
240  up,
241  down
242  };
243 
244  Malloc mm(*stats);
245 
246  while (total_allocation < opt_lo_water)
247  {
248  unsigned alloc_bytes;
249  int8_t* ptr = (int8_t*)mm.malloc_opt(&alloc_bytes);
250  std::pair<void*, int> xx(ptr, alloc_bytes);
251  ptr_vec.push_back(xx);
252  total_allocation += alloc_bytes;
253  }
254  direction = up;
255 
256  printf("ptr_vec.size()=%ld\n", ptr_vec.size());
257  while (!g_stop)
258  {
259  long rr = random();
260  // for the "up" direction, higher probability for malloc
261  long tst = (direction == up) ? RAND_MAX / 4 * 3 : RAND_MAX / 4 * 1;
262  int malloc1_or_free0 = rr < tst;
263 
264  if (malloc1_or_free0)
265  {
266  // do mallocs
267  unsigned alloc_bytes;
268  int8_t* ptr = (int8_t*)mm.malloc_opt(&alloc_bytes);
269  std::pair<void*, int> xx(ptr, alloc_bytes);
270  ptr_vec.push_back(xx);
271  total_allocation += alloc_bytes;
272  if (total_allocation > opt_hi_water && direction == up)
273  {
274  TLOG(TLVL_WARNING) << "direction=down";
275  direction = down;
276  }
277  }
278  else
279  {
280  // do frees
281  if (ptr_vec.size() == 0)
282  {
283  printf("size()==0 - continue\n");
284  continue;
285  }
286  unsigned free_idx = random() % ptr_vec.size();
287  std::pair<void*, int> xx(ptr_vec[free_idx]);
288  ptr_vec.erase(ptr_vec.begin() + free_idx);
289  TLOG(TLVL_INFO) << "free " << xx.second;
290  free(xx.first);
291  total_allocation -= xx.second;
292  if (total_allocation < opt_lo_water && direction == down)
293  {
294  TLOG(TLVL_WARNING) << "direction=up";
295  direction = up;
296  }
297  }
298  }
299 }
300 
301 void send_metrics(double ave, double min, double max, double MBperalloc, int32_t threads)
302 {
303 # define CLUSTER "mu2e DAQ"
304 # define GROUP "memory"
305  send_gmetric("ave", std::to_string(ave).c_str(), "double", "us/MB"
306  , "both", 30, 0, GROUP, CLUSTER, "ave us/MB", "ave us/MB");
307  send_gmetric("min", std::to_string(min).c_str(), "double", "us/MB"
308  , "both", 30, 0, GROUP, CLUSTER, "min us/MB", "min us/MB");
309  send_gmetric("max", std::to_string(max).c_str(), "double", "us/MB"
310  , "both", 30, 0, GROUP, CLUSTER, "max us/MB", "max us/MB");
311  send_gmetric("ave_MBperalloc", std::to_string(MBperalloc).c_str(), "double", "MB/alloc"
312  , "both", 30, 0, GROUP, CLUSTER, "ave MB/alloc", "ave MB/alloc");
313  send_gmetric("threads", std::to_string(threads).c_str(), "int32", "threads"
314  , "both", 30, 0, GROUP, CLUSTER, "threads mallocing", "threads mallocing");
315 }
316 
317 
318 int main(int argc, char* argv[])
319 {
320  parse_args(argc, argv);
321 
322  if (opt_hi_water < opt_lo_water)
323  {
324  printf("changeing hi_water from %lu to %lu\n", opt_hi_water, opt_lo_water);
325  opt_hi_water = opt_lo_water;
326  }
327  if (opt_max_alloc < opt_min_alloc)
328  {
329  printf("changeing max_alloc from %u to %u\n", opt_max_alloc, opt_min_alloc);
330  opt_max_alloc = opt_min_alloc;
331  }
332 
333  if (!opt_seed_)
334  opt_seed = time(NULL) * getpid();
335  srandom(opt_seed);
336  printf("opt_seed=%u RAND_MAX=%d random=%ld\n", opt_seed, RAND_MAX, random());
337  printf("lo=%ld hi=%ld min=%u max=%u\n", opt_lo_water, opt_hi_water, opt_min_alloc, opt_max_alloc);
338 
339  init_gmetric("/etc/ganglia/gmond.conf");
340 
341 
342  std::vector<boost::thread> threads(opt_threads);
343  std::vector<Stats> stats(opt_threads);
344  for (auto nn = 0; nn < opt_threads; ++nn)
345  {
346  boost::thread::attributes attrs;
347  attrs.set_stack_size(4096 * 2000); // 8000 KB
348  try {
349  threads[nn] = boost::thread(attrs, boost::bind(&do_work, &stats[nn]));
350  }
351  catch (const boost::exception& e)
352  {
353  TLOG(TLVL_ERROR) << "Caught boost::exception starting malloc_free thread: " << boost::diagnostic_information(e) << ", errno=" << errno;
354  std::cerr << "Caught boost::exception starting malloc_free thread: " << boost::diagnostic_information(e) << ", errno=" << errno << std::endl;
355  exit(5);
356  }
357  }
358 
359  uint64_t start_us = gettimeofday_us();
360  uint64_t end_us = start_us + (1000000L * opt_time);
361  usleep(5000000);
362  for (auto cc = 2; 1; ++cc)
363  {
364  double tot_ave_usperMB = 0.0, tot_min_usperMB = 0.0, tot_max_usperMB = 0.0, tot_MBperalloc = 0.0;
365  for (auto nn = 0; nn < opt_threads; ++nn)
366  {
367  double tot_MB;
368  int64_t tot_us;
369  int count;
370  double min, max;
371  stats[nn].get_clear(tot_MB, tot_us, count, min, max);
372  tot_ave_usperMB += (tot_MB != 0.0) ? (double)tot_us / tot_MB : 0.0;
373  tot_min_usperMB += min;
374  tot_max_usperMB += max;
375  tot_MBperalloc += count ? tot_MB / count : 0;
376  TLOG(6) << "nn=" << nn << " tot_MB=" << std::setprecision(4) << tot_MB << " count=" << count;
377  }
378  if ((cc % 5) == 0)
379  TLOG(TLVL_ERROR) << opt_threads
380  << " ave malloc usec/MB ave=" << std::setprecision(4) << tot_ave_usperMB / opt_threads
381  << " min=" << tot_min_usperMB / opt_threads << " max=" << tot_max_usperMB / opt_threads
382  << " MB/alloc=" << std::setprecision(1) << tot_MBperalloc / opt_threads;
383 
384  send_metrics(tot_ave_usperMB / opt_threads
385  , tot_min_usperMB / opt_threads
386  , tot_max_usperMB / opt_threads
387  , tot_MBperalloc / opt_threads
388  , opt_threads);
389 
390  uint64_t target = start_us + (5000000L * cc);
391  uint64_t now_us = gettimeofday_us();
392  int64_t slp = target - now_us;
393  if (slp > 0)
394  {
395  if ((now_us + slp) > end_us)
396  break;
397  usleep(slp);
398  }
399  else
400  {
401  printf("cc=%d slp=%ld\n", cc, slp);
402  if (now_us > end_us)
403  break;
404  }
405  }
406  send_metrics(0.0, 0.0, 0.0, 0.0, 0);
407  g_stop = 1;
408  for (auto nn = 0; nn < opt_threads; ++nn)
409  {
410  threads[nn].join();
411  }
412  printf("malloc_free finished.\n");
413  return (0);
414 } // main
int init_gmetric(const char *conf)
Initialize Ganglia.
Definition: send_gmetric.c:18
int send_gmetric(const char *name, const char *value, const char *type, const char *units, const char *slope, int tmax, int dmax, const char *group, const char *cluster, const char *desc, const char *title)
Send a metric to gmond.
Definition: send_gmetric.c:53