memprof 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/ext/util.c CHANGED
@@ -1,10 +1,13 @@
1
+ #include <stdlib.h>
2
+ #include <time.h>
3
+ #include <util.h>
4
+
5
+ #include <sys/time.h>
6
+
1
7
  /* This is the CRC function used by GNU. Stripped executables may contain a
2
8
  * section .gnu_debuglink which holds the name of an elf object with debug
3
9
  * information and a checksum.
4
- */
5
- #include <stdlib.h>
6
- #include <util.h>
7
- /* !!!! DO NOT MODIFY THIS FUNCTION !!!!
10
+ * !!!! DO NOT MODIFY THIS FUNCTION !!!!
8
11
  * TODO create specs for this!
9
12
  */
10
13
  unsigned long
@@ -71,3 +74,18 @@ gnu_debuglink_crc32(unsigned long crc, unsigned char *buf, size_t len)
71
74
  crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
72
75
  return ~crc & 0xffffffff;
73
76
  }
77
+
78
+ double
79
+ timeofday()
80
+ {
81
+ struct timeval tv;
82
+ #ifdef CLOCK_MONOTONIC
83
+ struct timespec tp;
84
+
85
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
86
+ return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9;
87
+ }
88
+ #endif
89
+ gettimeofday(&tv, NULL);
90
+ return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
91
+ }
data/ext/util.h CHANGED
@@ -73,4 +73,9 @@ struct memprof_config {
73
73
  unsigned long
74
74
  gnu_debuglink_crc32 (unsigned long crc, unsigned char *buf, size_t len);
75
75
 
76
+ /* Use this function for time tracking. It will (interally) try to use an
77
+ * appropriately granual timing function.
78
+ */
79
+ double
80
+ timeofday();
76
81
  #endif
@@ -27,14 +27,14 @@ arch_insert_st1_tramp(void *start, void *trampee, void *tramp)
27
27
  assert(tramp != NULL);
28
28
 
29
29
  int32_t fn_addr = 0;
30
+ int32_t offset = 0;
30
31
  struct st1_base *check = start;
31
32
 
32
33
  if (check->call == 0xe8) {
33
34
  fn_addr = check->displacement;
34
35
  if ((trampee - (void *)(check + 1)) == fn_addr) {
35
- WRITE_INSTRUCTIONS(&check->displacement,
36
- sizeof(*check),
37
- (check->displacement = (tramp - (void *)(check + 1))));
36
+ offset = tramp - (void *)(check + 1);
37
+ copy_instructions(&check->displacement, &offset, sizeof(offset));
38
38
  return 0;
39
39
  }
40
40
  }
@@ -75,17 +75,4 @@ copy_instructions(void *dest, void *src, size_t count)
75
75
  */
76
76
  return;
77
77
  }
78
-
79
- /*
80
- * WRITE_INSTRUCTIONS - page align start, recalculate len to take into account
81
- * alignment, set the read/write permissions and execute the code stmt.
82
- */
83
- #define WRITE_INSTRUCTIONS(start, len, stmt) do { \
84
- void *aligned_addr = page_align((void *)start); \
85
- int count = ((void *)start) - aligned_addr + len; \
86
- mprotect(aligned_addr, count, PROT_READ | PROT_WRITE | PROT_EXEC); \
87
- stmt; \
88
- mprotect(aligned_addr, count, PROT_READ | PROT_EXEC); \
89
- } while (0)
90
-
91
78
  #endif
@@ -0,0 +1,34 @@
1
+ begin
2
+ require File.expand_path('../../memprof', __FILE__)
3
+ rescue LoadError
4
+ require File.expand_path('../../../ext/memprof', __FILE__)
5
+ end
6
+
7
+ module Memprof
8
+ # Middleware for tracing requests
9
+ #
10
+ # require 'memprof/tracer'
11
+ # config.middleware.use(Memprof::Tracer)
12
+ class Tracer
13
+ def initialize(app)
14
+ @app=app
15
+ end
16
+ def call(env)
17
+ Memprof.trace_filename ||= "/tmp/memprof_tracer-#{Process.pid}.json"
18
+ Memprof.trace_request(env){ @app.call(env) }
19
+ end
20
+ end
21
+
22
+ # Legacy filter for tracing requests on Rails 2.2
23
+ #
24
+ # require 'memprof/tracer'
25
+ # around_filter(Memprof::Filter)
26
+ module Filter
27
+ def self.filter(controller)
28
+ env = controller.request.env
29
+ info = controller.request.path_parameters
30
+ Memprof.trace_filename ||= "/tmp/memprof_tracer-#{Process.pid}.json"
31
+ Memprof.trace_request(env.merge('action_controller.request.path_parameters' => info)){ yield }
32
+ end
33
+ end
34
+ end
@@ -1,6 +1,6 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'memprof'
3
- s.version = '0.3.2'
3
+ s.version = '0.3.3'
4
4
  s.date = '2010-04-13'
5
5
  s.summary = 'Ruby Memory Profiler'
6
6
  s.description = "Ruby memory profiler similar to bleak_house, but without patches to the Ruby VM"
@@ -61,10 +61,10 @@ describe Memprof do
61
61
  1.23+1
62
62
  Memprof.dump(filename)
63
63
 
64
- filedata.should =~ /"file": "#{__FILE__}"/
65
- filedata.should =~ /"line": #{__LINE__-4}/
66
- filedata.should =~ /"type": "float"/
67
- filedata.should =~ /"data": 2\.23/
64
+ filedata.should =~ /"file":"#{__FILE__}"/
65
+ filedata.should =~ /"line":#{__LINE__-4}/
66
+ filedata.should =~ /"type":"float"/
67
+ filedata.should =~ /"data":2\.23/
68
68
  end
69
69
 
70
70
  should 'raise error when calling ::stats or ::dump without ::start' do
@@ -72,6 +72,14 @@ describe Memprof do
72
72
  lambda{ Memprof.dump }.should.raise(RuntimeError).message.should =~ /Memprof.start/
73
73
  end
74
74
 
75
+ should 'dump objects created for block' do
76
+ Memprof.dump(filename) do
77
+ 2.45+1
78
+ end
79
+
80
+ filedata.should =~ /"data":3\.45/
81
+ end
82
+
75
83
  should 'dump out the entire heap' do
76
84
  Memprof.stop
77
85
  Memprof.dump_all(filename)
@@ -0,0 +1,135 @@
1
+ require File.dirname(__FILE__) + "/../ext/memprof"
2
+
3
+ require 'rubygems'
4
+ require 'bacon'
5
+ Bacon.summary_on_exit
6
+
7
+ require 'tempfile'
8
+
9
+ # XXX must require upfront before tracers are installed
10
+ require 'socket'
11
+ require 'open-uri'
12
+ begin; require 'mysql'; rescue LoadError; end
13
+ begin; require 'memcached'; rescue LoadError; end
14
+
15
+ describe 'Memprof tracers' do
16
+ @tempfile = Tempfile.new('tracing_spec')
17
+
18
+ def filename
19
+ @tempfile.path
20
+ end
21
+
22
+ def filedata
23
+ File.read(filename)
24
+ end
25
+
26
+ should 'trace i/o for block' do
27
+ Memprof.trace(filename) do
28
+ open("http://google.com").read
29
+ end
30
+
31
+ filedata.should =~ /"read":\{"calls":\d+/
32
+ filedata.should =~ /"write":\{"calls":\d+/
33
+ filedata.should =~ /"connect":\{"calls":\d+/
34
+ end
35
+
36
+ should 'trace select for block' do
37
+ Memprof.trace(filename) do
38
+ select(nil, nil, nil, 0.15)
39
+ end
40
+
41
+ filedata.should =~ /"select":\{"calls":1,"time":0\.1[567]/
42
+ time = filedata[/"select":\{"calls":\d+,"time":([\d.]+)/, 1].to_f
43
+ time.should.be.close(0.15, 0.1)
44
+ end
45
+
46
+ should 'trace objects created for block' do
47
+ Memprof.trace(filename) do
48
+ 10.times{1.1+1.2}
49
+ end
50
+
51
+ filedata.should =~ /"float":10/
52
+ end
53
+
54
+ should 'trace gc runs for block' do
55
+ Memprof.trace(filename) do
56
+ 10.times{GC.start}
57
+ end
58
+
59
+ filedata.should =~ /"gc":\{"calls":10,"time":[\d.]+/
60
+ end
61
+
62
+ should 'trace memory allocation for block' do
63
+ Memprof.trace(filename) do
64
+ 10.times{ "abc" << "def" }
65
+ end
66
+
67
+ filedata.should =~ /"malloc":\{"calls":10/
68
+ filedata.should =~ /"realloc":\{"calls":10/
69
+ end
70
+
71
+ if defined? Mysql
72
+ begin
73
+ conn = Mysql.connect('localhost', 'root')
74
+
75
+ should 'trace mysql calls for block' do
76
+ Memprof.trace(filename) do
77
+ 5.times{ conn.query("select sleep(0.05)") }
78
+ end
79
+
80
+ filedata.should =~ /"mysql":\{"queries":5,"time":([\d.]+)/
81
+ time = filedata[/"mysql":\{"queries":5,"time":([\d.]+)/, 1].to_f
82
+ time.should.be.close(0.25, 0.1)
83
+ end
84
+ rescue Mysql::Error => e
85
+ raise unless e.message =~ /connect/
86
+ end
87
+ end
88
+
89
+ if defined? Memcached
90
+ begin
91
+ conn = Memcached.new("localhost:11211", :show_backtraces => true)
92
+ conn.stats
93
+
94
+ should 'trace memcached calls for block' do
95
+ Memprof.trace(filename) do
96
+ conn.delete("memprof") rescue nil
97
+ conn.get("memprof") rescue nil
98
+ conn.set("memprof", "is cool")
99
+ conn.get("memprof")
100
+ end
101
+
102
+ filedata.should =~ /"memcache":\{"get":\{"calls":2,"responses":\{"success":1,"notfound":1/
103
+ filedata.should =~ /"set":\{"calls":1,"responses":\{"success":1/
104
+ end
105
+ rescue Memcached::SomeErrorsWereReported
106
+ end
107
+ end
108
+ end
109
+
110
+ describe 'Memprof request tracing' do
111
+ @tempfile = Tempfile.new('tracing_spec')
112
+
113
+ def filename
114
+ @tempfile.path
115
+ end
116
+
117
+ def filedata
118
+ File.read(filename)
119
+ end
120
+
121
+ should 'trace request env' do
122
+ env = {"REQUEST_PATH" => "value"}
123
+
124
+ Memprof.trace_filename = filename
125
+ Memprof.trace_filename.should == filename
126
+
127
+ Memprof.trace_request(env) do
128
+ end
129
+
130
+ Memprof.trace_filename = nil
131
+ Memprof.trace_filename.should.be.nil
132
+
133
+ filedata.should =~ /"REQUEST_PATH":"value"/
134
+ end
135
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 3
8
- - 2
9
- version: 0.3.2
8
+ - 3
9
+ version: 0.3.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Joe Damato
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2010-04-13 00:00:00 -04:00
20
+ date: 2010-04-13 00:00:00 -07:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency
@@ -68,14 +68,22 @@ files:
68
68
  - ext/extconf.rb
69
69
  - ext/i386.c
70
70
  - ext/i386.h
71
+ - ext/json.c
72
+ - ext/json.h
71
73
  - ext/mach.c
72
74
  - ext/memprof.c
75
+ - ext/mmap.h
73
76
  - ext/src/libdwarf-20091118.tar.gz
74
77
  - ext/src/libelf-0.8.13.tar.gz
75
78
  - ext/src/yajl-1.0.9.tar.gz
76
79
  - ext/tracer.c
77
80
  - ext/tracer.h
78
- - ext/tracers/malloc.c
81
+ - ext/tracers/fd.c
82
+ - ext/tracers/gc.c
83
+ - ext/tracers/memcache.c
84
+ - ext/tracers/memory.c
85
+ - ext/tracers/mysql.c
86
+ - ext/tracers/objects.c
79
87
  - ext/tramp.c
80
88
  - ext/tramp.h
81
89
  - ext/util.c
@@ -86,9 +94,11 @@ files:
86
94
  - ext/x86_gen.h
87
95
  - lib/memprof/middleware.rb
88
96
  - lib/memprof/signal.rb
97
+ - lib/memprof/tracer.rb
89
98
  - memprof.gemspec
90
99
  - spec/memprof_spec.rb
91
100
  - spec/memprof_uploader_spec.rb
101
+ - spec/tracing_spec.rb
92
102
  has_rdoc: true
93
103
  homepage: http://github.com/ice799/memprof
94
104
  licenses: []
@@ -1,163 +0,0 @@
1
- #include <assert.h>
2
- #include <stdio.h>
3
- #include <stdlib.h>
4
- #include <string.h>
5
-
6
- #include "arch.h"
7
- #include "bin_api.h"
8
- #include "tracer.h"
9
- #include "util.h"
10
-
11
- struct memprof_malloc_stats {
12
- size_t malloc_bytes_requested;
13
- size_t calloc_bytes_requested;
14
- size_t realloc_bytes_requested;
15
-
16
- size_t malloc_bytes_actual;
17
- size_t calloc_bytes_actual;
18
- size_t realloc_bytes_actual;
19
- size_t free_bytes_actual;
20
-
21
- size_t malloc_calls;
22
- size_t calloc_calls;
23
- size_t realloc_calls;
24
- size_t free_calls;
25
- };
26
-
27
- static struct tracer tracer;
28
- static struct memprof_malloc_stats memprof_malloc_stats;
29
- static void *(*orig_malloc)(size_t), *(*orig_realloc)(void *, size_t),
30
- *(*orig_calloc)(size_t, size_t), (*orig_free)(void *);
31
- static size_t (*malloc_usable_size)(void *ptr);
32
-
33
- static void *
34
- malloc_tramp(size_t size)
35
- {
36
- void *ret = NULL;
37
- memprof_malloc_stats.malloc_bytes_requested += size;
38
- memprof_malloc_stats.malloc_calls++;
39
- ret = orig_malloc(size);
40
- memprof_malloc_stats.malloc_bytes_actual += malloc_usable_size(ret);
41
- return ret;
42
- }
43
-
44
- static void *
45
- calloc_tramp(size_t nmemb, size_t size)
46
- {
47
- void *ret = NULL;
48
- memprof_malloc_stats.calloc_bytes_requested += (nmemb * size);
49
- memprof_malloc_stats.calloc_calls++;
50
- ret = (*orig_calloc)(nmemb, size);
51
- memprof_malloc_stats.calloc_bytes_actual += malloc_usable_size(ret);
52
- return ret;
53
- }
54
-
55
- static void *
56
- realloc_tramp(void *ptr, size_t size)
57
- {
58
- /* TODO need to check malloc_usable_size of before/after i guess? */
59
- void *ret = NULL;
60
- memprof_malloc_stats.realloc_bytes_requested += size;
61
- memprof_malloc_stats.realloc_calls++;
62
- ret = orig_realloc(ptr, size);
63
- memprof_malloc_stats.realloc_bytes_actual += malloc_usable_size(ptr);
64
- return ret;
65
- }
66
-
67
- static void
68
- free_tramp(void *ptr)
69
- {
70
- memprof_malloc_stats.free_bytes_actual += malloc_usable_size(ptr);
71
- memprof_malloc_stats.free_calls++;
72
- orig_free(ptr);
73
- }
74
-
75
- static void
76
- malloc_trace_stop()
77
- {
78
- struct tramp_st2_entry tmp;
79
-
80
- tmp.addr = orig_malloc;
81
- bin_update_image("malloc", &tmp, NULL);
82
-
83
- tmp.addr = orig_realloc;
84
- bin_update_image("realloc", &tmp, NULL);
85
-
86
- if (orig_calloc) {
87
- tmp.addr = orig_calloc;
88
- bin_update_image("calloc", &tmp, NULL);
89
- }
90
-
91
- tmp.addr = orig_free;
92
- bin_update_image("free", &tmp, NULL);
93
- }
94
-
95
- static void
96
- malloc_trace_reset()
97
- {
98
- memset(&memprof_malloc_stats, 0, sizeof(memprof_malloc_stats));
99
- }
100
-
101
- static void
102
- malloc_trace_dump()
103
- {
104
- fprintf(stderr, "================ Requested ====================\n");
105
- fprintf(stderr, "Malloced: %zd, Realloced: %zd, Calloced: %zd\n",
106
- memprof_malloc_stats.malloc_bytes_requested, memprof_malloc_stats.realloc_bytes_requested,
107
- memprof_malloc_stats.calloc_bytes_requested);
108
- fprintf(stderr, "================ Actual ====================\n");
109
- fprintf(stderr, "Malloced: %zd, Realloced: %zd, Calloced: %zd, Freed: %zd\n",
110
- memprof_malloc_stats.malloc_bytes_actual, memprof_malloc_stats.realloc_bytes_actual,
111
- memprof_malloc_stats.calloc_bytes_actual, memprof_malloc_stats.free_bytes_actual);
112
- fprintf(stderr, "================ Call count ====================\n");
113
- fprintf(stderr, "Calls to malloc: %zd, realloc: %zd, calloc: %zd, free: %zd\n",
114
- memprof_malloc_stats.malloc_calls,
115
- memprof_malloc_stats.realloc_calls,
116
- memprof_malloc_stats.calloc_calls,
117
- memprof_malloc_stats.free_calls);
118
- }
119
-
120
- static void
121
- malloc_trace_start()
122
- {
123
- struct tramp_st2_entry tmp;
124
-
125
- if (!malloc_usable_size) {
126
- if ((malloc_usable_size =
127
- bin_find_symbol("MallocExtension_GetAllocatedSize", NULL, 1)) == NULL) {
128
- malloc_usable_size = bin_find_symbol("malloc_usable_size", NULL, 1);
129
- dbg_printf("tcmalloc was not found...\n");
130
- }
131
- assert(malloc_usable_size != NULL);
132
- dbg_printf("malloc_usable_size: %p\n", malloc_usable_size);
133
- }
134
-
135
- tmp.addr = malloc_tramp;
136
- bin_update_image("malloc", &tmp, (void **)(&orig_malloc));
137
- assert(orig_malloc != NULL);
138
- dbg_printf("orig_malloc: %p\n", orig_malloc);
139
-
140
- tmp.addr = realloc_tramp;
141
- bin_update_image("realloc", &tmp,(void **)(&orig_realloc));
142
- dbg_printf("orig_realloc: %p\n", orig_realloc);
143
-
144
- tmp.addr = calloc_tramp;
145
- bin_update_image("calloc", &tmp, (void **)(&orig_calloc));
146
- dbg_printf("orig_calloc: %p\n", orig_calloc);
147
-
148
- tmp.addr = free_tramp;
149
- bin_update_image("free", &tmp, (void **)(&orig_free));
150
- assert(orig_free != NULL);
151
- dbg_printf("orig_free: %p\n", orig_free);
152
- }
153
-
154
- void install_malloc_tracer()
155
- {
156
- tracer.start = malloc_trace_start;
157
- tracer.stop = malloc_trace_stop;
158
- tracer.reset = malloc_trace_reset;
159
- tracer.dump = malloc_trace_dump;
160
- tracer.id = strdup("malloc_tracer");
161
-
162
- trace_insert(&tracer);
163
- }