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