1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
2    	// vim: ts=8 sw=2 smarttab
3    	/*
4    	 * Ceph - scalable distributed file system
5    	 *
6    	 * Copyright (C) 2011 New Dream Network/Sage Weil <sage@newdream.net>
7    	 *
8    	 * This is free software; you can redistribute it and/or
9    	 * modify it under the terms of the GNU Lesser General Public
10   	 * License version 2.1, as published by the Free Software 
11   	 * Foundation.  See file COPYING.
12   	 * 
13   	 */
14   	
15   	#include "acconfig.h"
16   	
17   	// Use the newer gperftools header locations if available.
18   	// If not, fall back to the old (gperftools < 2.0) locations.
19   	
20   	#include <gperftools/heap-profiler.h>
21   	#include <gperftools/malloc_extension.h>
22   	
23   	#include "heap_profiler.h"
24   	#include "common/environment.h"
25   	#include "common/LogClient.h"
26   	#include "global/global_context.h"
27   	#include "common/debug.h"
28   	
29   	#define dout_context g_ceph_context
30   	
31   	bool ceph_using_tcmalloc()
32   	{
33   	  return true;
34   	}
35   	
36   	void ceph_heap_profiler_init()
37   	{
38   	  // Two other interesting environment variables to set are:
39   	  // HEAP_PROFILE_ALLOCATION_INTERVAL, HEAP_PROFILE_INUSE_INTERVAL
40   	  if (get_env_bool("CEPH_HEAP_PROFILER_INIT")) {
41   	    ceph_heap_profiler_start();
42   	  }
43   	}
44   	
45   	void ceph_heap_profiler_stats(char *buf, int length)
46   	{
47   	  MallocExtension::instance()->GetStats(buf, length);
48   	}
49   	
50   	void ceph_heap_release_free_memory()
51   	{
52   	  MallocExtension::instance()->ReleaseFreeMemory();
53   	}
54   	
55   	double ceph_heap_get_release_rate()
56   	{
57   	  return MallocExtension::instance()->GetMemoryReleaseRate();
58   	}
59   	
60   	void ceph_heap_set_release_rate(double val)
61   	{
62   	  MallocExtension::instance()->SetMemoryReleaseRate(val);
63   	}
64   	
65   	bool ceph_heap_get_numeric_property(
66   	  const char *property, size_t *value)
67   	{
68   	  return MallocExtension::instance()->GetNumericProperty(
69   	    property,
70   	    value);
71   	}
72   	
73   	bool ceph_heap_set_numeric_property(
74   	  const char *property, size_t value)
75   	{
76   	  return MallocExtension::instance()->SetNumericProperty(
77   	    property,
78   	    value);
79   	}
80   	
81   	bool ceph_heap_profiler_running()
82   	{
83   	#ifdef HAVE_LIBTCMALLOC
84   	  return IsHeapProfilerRunning();
85   	#else
86   	  return false;
87   	#endif
88   	}
89   	
90   	static void get_profile_name(char *profile_name, int profile_name_len)
91   	{
92   	#if __GNUC__ && __GNUC__ >= 8
93   	#pragma GCC diagnostic push
94   	  // Don't care, it doesn't matter, and we can't do anything about it.
95   	#pragma GCC diagnostic ignored "-Wformat-truncation"
96   	#endif
97   	
98   	  char path[PATH_MAX];
99   	  snprintf(path, sizeof(path), "%s", g_conf()->log_file.c_str());
100  	  char *last_slash = rindex(path, '/');
101  	
102  	  if (last_slash == NULL) {
103  	    snprintf(profile_name, profile_name_len, "./%s.profile",
104  		     g_conf()->name.to_cstr());
105  	  }
106  	  else {
107  	    last_slash[1] = '\0';
108  	    snprintf(profile_name, profile_name_len, "%s/%s.profile",
109  		     path, g_conf()->name.to_cstr());
110  	  }
111  	#if __GNUC__ && __GNUC__ >= 8
112  	#pragma GCC diagnostic pop
113  	#endif
114  	}
115  	
116  	void ceph_heap_profiler_start()
117  	{
118  	#ifdef HAVE_LIBTCMALLOC
119  	  char profile_name[PATH_MAX];
120  	  get_profile_name(profile_name, sizeof(profile_name)); 
121  	  generic_dout(0) << "turning on heap profiler with prefix "
122  			  << profile_name << dendl;
123  	  HeapProfilerStart(profile_name);
124  	#endif
125  	}
126  	
127  	void ceph_heap_profiler_stop()
128  	{
129  	#ifdef HAVE_LIBTCMALLOC
130  	  HeapProfilerStop();
131  	#endif
132  	}
133  	
134  	void ceph_heap_profiler_dump(const char *reason)
135  	{
136  	#ifdef HAVE_LIBTCMALLOC
137  	  HeapProfilerDump(reason);
138  	#endif
139  	}
140  	
141  	#define HEAP_PROFILER_STATS_SIZE 2048
142  	
143  	void ceph_heap_profiler_handle_command(const std::vector<std::string>& cmd,
144  	                                       ostream& out)
145  	{
146  	#ifdef HAVE_LIBTCMALLOC
(1) Event cond_true: Condition "cmd->size() == 1", taking true branch.
(2) Event cond_false: Condition "cmd[0] == "dump"", taking false branch.
147  	  if (cmd.size() == 1 && cmd[0] == "dump") {
148  	    if (!ceph_heap_profiler_running()) {
149  	      out << "heap profiler not running; can't dump";
150  	      return;
151  	    }
152  	    char heap_stats[HEAP_PROFILER_STATS_SIZE];
153  	    ceph_heap_profiler_stats(heap_stats, sizeof(heap_stats));
154  	    out << g_conf()->name << " dumping heap profile now.\n"
155  		<< heap_stats;
156  	    ceph_heap_profiler_dump("admin request");
(3) Event else_branch: Reached else branch.
(4) Event cond_true: Condition "cmd->size() == 1", taking true branch.
(5) Event cond_false: Condition "cmd[0] == "start_profiler"", taking false branch.
157  	  } else if (cmd.size() == 1 && cmd[0] == "start_profiler") {
158  	    ceph_heap_profiler_start();
159  	    out << g_conf()->name << " started profiler";
(6) Event else_branch: Reached else branch.
(7) Event cond_true: Condition "cmd->size() == 1", taking true branch.
(8) Event cond_false: Condition "cmd[0] == "stop_profiler"", taking false branch.
160  	  } else if (cmd.size() == 1 && cmd[0] == "stop_profiler") {
161  	    ceph_heap_profiler_stop();
162  	    out << g_conf()->name << " stopped profiler";
(9) Event else_branch: Reached else branch.
(10) Event cond_true: Condition "cmd->size() == 1", taking true branch.
(11) Event cond_false: Condition "cmd[0] == "release"", taking false branch.
163  	  } else if (cmd.size() == 1 && cmd[0] == "release") {
164  	    ceph_heap_release_free_memory();
165  	    out << g_conf()->name << " releasing free RAM back to system.";
(12) Event else_branch: Reached else branch.
(13) Event cond_true: Condition "cmd->size() == 1", taking true branch.
(14) Event cond_true: Condition "cmd[0] == "get_release_rate"", taking true branch.
166  	  } else if (cmd.size() == 1 && cmd[0] == "get_release_rate") {
(15) Event format_changed: "setprecision" changes the format state of "out" for category precision.
Also see events: [end_of_path]
167  	    out << g_conf()->name << " release rate: " 
168  		<< std::setprecision(4) << ceph_heap_get_release_rate() << "\n";
(16) Event if_fallthrough: Falling through to end of if statement.
169  	  } else if (cmd.size() == 2 && cmd[0] == "set_release_rate") {
170  	    try {
171  	      double val = std::stod(cmd[1]);
172  	      ceph_heap_set_release_rate(val);
173  	      out << g_conf()->name <<  " release rate changed to: " 
174  	          << std::setprecision(4) << ceph_heap_get_release_rate() << "\n";
175  	    } catch (...) {
176  	      out << g_conf()->name <<  " *** need an numerical value. ";
177  	    }
178  	  } else
179  	#endif
180  	  if (cmd.size() == 1 && cmd[0] == "stats") {
181  	    char heap_stats[HEAP_PROFILER_STATS_SIZE];
182  	    ceph_heap_profiler_stats(heap_stats, sizeof(heap_stats));
183  	    out << g_conf()->name << " tcmalloc heap stats:"
184  		<< heap_stats;
185  	  } else {
186  	    out << "unknown command " << cmd;
(17) Event if_end: End of if statement.
187  	  }
(18) Event end_of_path: Changing format state of stream "out" for category precision without later restoring it.
Also see events: [format_changed]
188  	}
189