ruby-dtrace-consumer 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENCE +20 -0
- data/README.md +51 -0
- data/ext/Makefile +187 -0
- data/ext/dtrace_aggdata.c +132 -0
- data/ext/dtrace_aggdata.c~ +141 -0
- data/ext/dtrace_aggdata.o +0 -0
- data/ext/dtrace_api.bundle +0 -0
- data/ext/dtrace_api.c +102 -0
- data/ext/dtrace_api.c~ +113 -0
- data/ext/dtrace_api.h +138 -0
- data/ext/dtrace_api.h~ +155 -0
- data/ext/dtrace_api.o +0 -0
- data/ext/dtrace_bufdata.c +130 -0
- data/ext/dtrace_bufdata.c~ +139 -0
- data/ext/dtrace_bufdata.o +0 -0
- data/ext/dtrace_dropdata.c +121 -0
- data/ext/dtrace_dropdata.c~ +131 -0
- data/ext/dtrace_dropdata.o +0 -0
- data/ext/dtrace_errdata.c +100 -0
- data/ext/dtrace_errdata.c~ +110 -0
- data/ext/dtrace_errdata.o +0 -0
- data/ext/dtrace_hdl.c +677 -0
- data/ext/dtrace_hdl.c~ +689 -0
- data/ext/dtrace_hdl.o +0 -0
- data/ext/dtrace_probedata.c +273 -0
- data/ext/dtrace_probedata.c~ +283 -0
- data/ext/dtrace_probedata.o +0 -0
- data/ext/dtrace_probedesc.c +93 -0
- data/ext/dtrace_probedesc.c~ +78 -0
- data/ext/dtrace_probedesc.o +0 -0
- data/ext/dtrace_process.c +44 -0
- data/ext/dtrace_process.c~ +56 -0
- data/ext/dtrace_process.o +0 -0
- data/ext/dtrace_program.c +52 -0
- data/ext/dtrace_program.c~ +62 -0
- data/ext/dtrace_program.o +0 -0
- data/ext/dtrace_programinfo.c +70 -0
- data/ext/dtrace_programinfo.c~ +60 -0
- data/ext/dtrace_programinfo.o +0 -0
- data/ext/dtrace_recdesc.c +37 -0
- data/ext/dtrace_recdesc.c~ +46 -0
- data/ext/dtrace_recdesc.o +0 -0
- data/ext/dtrace_util.c +92 -0
- data/ext/dtrace_util.o +0 -0
- data/ext/extconf.rb +7 -0
- data/lib/dtrace.rb +95 -0
- data/lib/dtrace/aggregate.rb +40 -0
- data/lib/dtrace/aggregateset.rb +19 -0
- data/lib/dtrace/consumer.rb +174 -0
- data/lib/dtrace/data.rb +85 -0
- data/lib/dtrace/dof.rb +8 -0
- data/lib/dtrace/printfrecord.rb +10 -0
- data/lib/dtrace/probedata.rb +23 -0
- data/lib/dtrace/probedesc.rb +15 -0
- data/lib/dtrace/record.rb +11 -0
- data/lib/dtrace/stackrecord.rb +31 -0
- data/lib/dtrace/tracer.rb +35 -0
- data/lib/dtrace/version.rb +8 -0
- data/lib/dtrace/version.rb~ +8 -0
- data/lib/dtraceconsumer.rb +9 -0
- data/test/test_aggregates.rb +45 -0
- data/test/test_drops_errors.rb +166 -0
- data/test/test_dtrace.rb +155 -0
- data/test/test_gc.rb +11 -0
- data/test/test_helper.rb +20 -0
- data/test/test_helper.rb~ +16 -0
- data/test/test_legacy_consumer.rb +47 -0
- data/test/test_probedata.rb +30 -0
- data/test/test_processes.rb +66 -0
- data/test/test_profile.rb +198 -0
- data/test/test_repeat.rb +50 -0
- data/test/test_rubyprobe.rb +52 -0
- data/test/test_rubyprobe.rb~ +52 -0
- data/test/test_typefilter.rb +94 -0
- metadata +121 -0
Binary file
|
@@ -0,0 +1,93 @@
|
|
1
|
+
/* Ruby-DTrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
RUBY_EXTERN VALUE cDTraceProbeDesc;
|
8
|
+
|
9
|
+
static void free_probedesc(void *p);
|
10
|
+
|
11
|
+
/* :nodoc: */
|
12
|
+
VALUE dtraceprobedesc_init(dtrace_probedesc_t *p)
|
13
|
+
{
|
14
|
+
dtrace_probedesc_t *pdp;
|
15
|
+
VALUE self;
|
16
|
+
|
17
|
+
pdp = ALLOC(dtrace_probedesc_t);
|
18
|
+
memcpy(pdp, p, sizeof(dtrace_probedesc_t));
|
19
|
+
|
20
|
+
self = Data_Wrap_Struct(cDTraceProbeDesc, 0, free_probedesc, pdp);
|
21
|
+
return self;
|
22
|
+
}
|
23
|
+
|
24
|
+
/*
|
25
|
+
* Returns the id of the probedesc. Corresponds to the ID displayed by
|
26
|
+
* dtrace -l
|
27
|
+
*/
|
28
|
+
VALUE dtraceprobedesc_probe_id(VALUE self)
|
29
|
+
{
|
30
|
+
dtrace_probedesc_t *pdp;
|
31
|
+
|
32
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
33
|
+
return INT2NUM(pdp->dtpd_id);
|
34
|
+
}
|
35
|
+
|
36
|
+
/*
|
37
|
+
* Returns the name of the probe's provider.
|
38
|
+
*/
|
39
|
+
VALUE dtraceprobedesc_provider(VALUE self)
|
40
|
+
{
|
41
|
+
VALUE string;
|
42
|
+
dtrace_probedesc_t *pdp;
|
43
|
+
|
44
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
45
|
+
string = rb_str_new2(pdp->dtpd_provider);
|
46
|
+
return string;
|
47
|
+
}
|
48
|
+
|
49
|
+
/*
|
50
|
+
* Returns the name of the module where the probe is defined.
|
51
|
+
*/
|
52
|
+
VALUE dtraceprobedesc_mod(VALUE self)
|
53
|
+
{
|
54
|
+
VALUE string;
|
55
|
+
dtrace_probedesc_t *pdp;
|
56
|
+
|
57
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
58
|
+
string = rb_str_new2(pdp->dtpd_mod);
|
59
|
+
return string;
|
60
|
+
}
|
61
|
+
|
62
|
+
/*
|
63
|
+
* Returns the name of the function where the probe is defined.
|
64
|
+
*/
|
65
|
+
VALUE dtraceprobedesc_func(VALUE self)
|
66
|
+
{
|
67
|
+
VALUE string;
|
68
|
+
dtrace_probedesc_t *pdp;
|
69
|
+
|
70
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
71
|
+
string = rb_str_new2(pdp->dtpd_func);
|
72
|
+
return string;
|
73
|
+
}
|
74
|
+
|
75
|
+
/*
|
76
|
+
* Returns the name of the probe.
|
77
|
+
*/
|
78
|
+
VALUE dtraceprobedesc_name(VALUE self)
|
79
|
+
{
|
80
|
+
VALUE string;
|
81
|
+
dtrace_probedesc_t *pdp;
|
82
|
+
|
83
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
84
|
+
string = rb_str_new2(pdp->dtpd_name);
|
85
|
+
return string;
|
86
|
+
}
|
87
|
+
|
88
|
+
static void free_probedesc(void *p)
|
89
|
+
{
|
90
|
+
dtrace_probedesc_t *pdp = p;
|
91
|
+
free(pdp);
|
92
|
+
}
|
93
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
/* Ruby-DTrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
/* :nodoc: */
|
8
|
+
VALUE dtraceprobedesc_init(VALUE self)
|
9
|
+
{
|
10
|
+
dtrace_probedesc_t *pdp;
|
11
|
+
|
12
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
13
|
+
return self;
|
14
|
+
}
|
15
|
+
|
16
|
+
/*
|
17
|
+
* Returns the id of the probedesc. Corresponds to the ID displayed by
|
18
|
+
* dtrace -l
|
19
|
+
*/
|
20
|
+
VALUE dtraceprobedesc_probe_id(VALUE self)
|
21
|
+
{
|
22
|
+
dtrace_probedesc_t *pdp;
|
23
|
+
|
24
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
25
|
+
return INT2NUM(pdp->dtpd_id);
|
26
|
+
}
|
27
|
+
|
28
|
+
/*
|
29
|
+
* Returns the name of the probe's provider.
|
30
|
+
*/
|
31
|
+
VALUE dtraceprobedesc_provider(VALUE self)
|
32
|
+
{
|
33
|
+
VALUE string;
|
34
|
+
dtrace_probedesc_t *pdp;
|
35
|
+
|
36
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
37
|
+
string = rb_str_new2(pdp->dtpd_provider);
|
38
|
+
return string;
|
39
|
+
}
|
40
|
+
|
41
|
+
/*
|
42
|
+
* Returns the name of the module where the probe is defined.
|
43
|
+
*/
|
44
|
+
VALUE dtraceprobedesc_mod(VALUE self)
|
45
|
+
{
|
46
|
+
VALUE string;
|
47
|
+
dtrace_probedesc_t *pdp;
|
48
|
+
|
49
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
50
|
+
string = rb_str_new2(pdp->dtpd_mod);
|
51
|
+
return string;
|
52
|
+
}
|
53
|
+
|
54
|
+
/*
|
55
|
+
* Returns the name of the function where the probe is defined.
|
56
|
+
*/
|
57
|
+
VALUE dtraceprobedesc_func(VALUE self)
|
58
|
+
{
|
59
|
+
VALUE string;
|
60
|
+
dtrace_probedesc_t *pdp;
|
61
|
+
|
62
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
63
|
+
string = rb_str_new2(pdp->dtpd_func);
|
64
|
+
return string;
|
65
|
+
}
|
66
|
+
|
67
|
+
/*
|
68
|
+
* Returns the name of the probe.
|
69
|
+
*/
|
70
|
+
VALUE dtraceprobedesc_name(VALUE self)
|
71
|
+
{
|
72
|
+
VALUE string;
|
73
|
+
dtrace_probedesc_t *pdp;
|
74
|
+
|
75
|
+
Data_Get_Struct(self, dtrace_probedesc_t, pdp);
|
76
|
+
string = rb_str_new2(pdp->dtpd_name);
|
77
|
+
return string;
|
78
|
+
}
|
Binary file
|
@@ -0,0 +1,44 @@
|
|
1
|
+
/* Ruby-DTrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
static void _release_process(dtrace_process_t *process)
|
8
|
+
{
|
9
|
+
if (process->handle->hdl != NULL && process->proc != NULL) {
|
10
|
+
dtrace_proc_release(process->handle->hdl, process->proc);
|
11
|
+
process->proc = NULL;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
/* :nodoc: */
|
16
|
+
void dtrace_process_free(dtrace_process_t *process)
|
17
|
+
{
|
18
|
+
_release_process(process);
|
19
|
+
free(process);
|
20
|
+
}
|
21
|
+
|
22
|
+
/* Release the traced process. */
|
23
|
+
VALUE dtrace_process_release(VALUE self)
|
24
|
+
{
|
25
|
+
dtrace_process_t *process;
|
26
|
+
|
27
|
+
Data_Get_Struct(self, dtrace_process_t, process);
|
28
|
+
_release_process(process);
|
29
|
+
return Qnil;
|
30
|
+
}
|
31
|
+
|
32
|
+
/*
|
33
|
+
* Start or restart the process. Call this having configured tracing
|
34
|
+
* for the process, using $target in the D program.
|
35
|
+
*/
|
36
|
+
VALUE dtrace_process_continue(VALUE self)
|
37
|
+
{
|
38
|
+
dtrace_process_t *process;
|
39
|
+
|
40
|
+
Data_Get_Struct(self, dtrace_process_t, process);
|
41
|
+
dtrace_proc_continue(process->handle->hdl, process->proc);
|
42
|
+
|
43
|
+
return Qnil;
|
44
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
/* Ruby-DTrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
/* :nodoc: */
|
8
|
+
VALUE dtrace_process_init(VALUE self)
|
9
|
+
{
|
10
|
+
dtrace_process_t *process;
|
11
|
+
|
12
|
+
Data_Get_Struct(self, dtrace_process_t, process);
|
13
|
+
if (process)
|
14
|
+
return self;
|
15
|
+
else
|
16
|
+
return Qnil;
|
17
|
+
}
|
18
|
+
|
19
|
+
static void _release_process(dtrace_process_t *process)
|
20
|
+
{
|
21
|
+
if (process->handle->hdl != NULL && process->proc != NULL) {
|
22
|
+
dtrace_proc_release(process->handle->hdl, process->proc);
|
23
|
+
process->proc = NULL;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
/* :nodoc: */
|
28
|
+
void dtrace_process_free(dtrace_process_t *process)
|
29
|
+
{
|
30
|
+
_release_process(process);
|
31
|
+
free(process);
|
32
|
+
}
|
33
|
+
|
34
|
+
/* Release the traced process. */
|
35
|
+
VALUE dtrace_process_release(VALUE self)
|
36
|
+
{
|
37
|
+
dtrace_process_t *process;
|
38
|
+
|
39
|
+
Data_Get_Struct(self, dtrace_process_t, process);
|
40
|
+
_release_process(process);
|
41
|
+
return Qnil;
|
42
|
+
}
|
43
|
+
|
44
|
+
/*
|
45
|
+
* Start or restart the process. Call this having configured tracing
|
46
|
+
* for the process, using $target in the D program.
|
47
|
+
*/
|
48
|
+
VALUE dtrace_process_continue(VALUE self)
|
49
|
+
{
|
50
|
+
dtrace_process_t *process;
|
51
|
+
|
52
|
+
Data_Get_Struct(self, dtrace_process_t, process);
|
53
|
+
dtrace_proc_continue(process->handle->hdl, process->proc);
|
54
|
+
|
55
|
+
return Qnil;
|
56
|
+
}
|
Binary file
|
@@ -0,0 +1,52 @@
|
|
1
|
+
/* Ruby-DTrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
RUBY_EXTERN VALUE eDTraceException;
|
8
|
+
|
9
|
+
/*
|
10
|
+
* Execute the D program. Returns a DTraceProgramInfo object if
|
11
|
+
* successful, otherwise raises a DTraceException.
|
12
|
+
*/
|
13
|
+
VALUE dtraceprogram_exec(VALUE self)
|
14
|
+
{
|
15
|
+
dtrace_prog_t *prog;
|
16
|
+
dtrace_proginfo_t *proginfo;
|
17
|
+
dtrace_handle_t *handle;
|
18
|
+
VALUE dtrace;
|
19
|
+
VALUE dtraceprograminfo;
|
20
|
+
int ret;
|
21
|
+
|
22
|
+
Data_Get_Struct(self, dtrace_prog_t, prog);
|
23
|
+
dtrace = rb_iv_get(self, "@handle");
|
24
|
+
Data_Get_Struct(dtrace, dtrace_handle_t, handle);
|
25
|
+
|
26
|
+
proginfo = ALLOC(dtrace_proginfo_t);
|
27
|
+
if (!proginfo) {
|
28
|
+
rb_raise(eDTraceException, "alloc failed");
|
29
|
+
return Qnil;
|
30
|
+
}
|
31
|
+
|
32
|
+
ret = dtrace_program_exec(handle->hdl, prog, proginfo);
|
33
|
+
|
34
|
+
if (ret == 0) {
|
35
|
+
dtraceprograminfo = dtraceprograminfo_init(proginfo);
|
36
|
+
rb_iv_set(self, "@proginfo", dtraceprograminfo);
|
37
|
+
}
|
38
|
+
|
39
|
+
if (ret < 0)
|
40
|
+
rb_raise(eDTraceException, "%s", dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
|
41
|
+
|
42
|
+
return Qnil;
|
43
|
+
}
|
44
|
+
|
45
|
+
/*
|
46
|
+
* Return this program's DTraceProgramInfo object. Returns nil unless
|
47
|
+
* the program has been executed.
|
48
|
+
*/
|
49
|
+
VALUE dtraceprogram_info(VALUE self)
|
50
|
+
{
|
51
|
+
return rb_iv_get(self, "@proginfo");
|
52
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
/* Ruby-DTrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
RUBY_EXTERN VALUE eDTraceException;
|
8
|
+
RUBY_EXTERN VALUE cDTraceProgramInfo;
|
9
|
+
|
10
|
+
/* :nodoc: */
|
11
|
+
VALUE dtraceprogram_init(VALUE self)
|
12
|
+
{
|
13
|
+
dtrace_prog_t *prog;
|
14
|
+
|
15
|
+
Data_Get_Struct(self, dtrace_prog_t, prog);
|
16
|
+
return self;
|
17
|
+
}
|
18
|
+
|
19
|
+
/*
|
20
|
+
* Execute the D program. Returns a DTraceProgramInfo object if
|
21
|
+
* successful, otherwise raises a DTraceException.
|
22
|
+
*/
|
23
|
+
VALUE dtraceprogram_exec(VALUE self)
|
24
|
+
{
|
25
|
+
dtrace_prog_t *prog;
|
26
|
+
dtrace_proginfo_t *proginfo;
|
27
|
+
dtrace_handle_t *handle;
|
28
|
+
VALUE dtrace;
|
29
|
+
VALUE dtraceprograminfo;
|
30
|
+
int ret;
|
31
|
+
|
32
|
+
Data_Get_Struct(self, dtrace_prog_t, prog);
|
33
|
+
dtrace = rb_iv_get(self, "@handle");
|
34
|
+
Data_Get_Struct(dtrace, dtrace_handle_t, handle);
|
35
|
+
|
36
|
+
proginfo = ALLOC(dtrace_proginfo_t);
|
37
|
+
if (!proginfo) {
|
38
|
+
rb_raise(eDTraceException, "alloc failed");
|
39
|
+
return Qnil;
|
40
|
+
}
|
41
|
+
|
42
|
+
ret = dtrace_program_exec(handle->hdl, prog, proginfo);
|
43
|
+
|
44
|
+
if (ret == 0) {
|
45
|
+
dtraceprograminfo = Data_Wrap_Struct(cDTraceProgramInfo, 0, NULL, proginfo);
|
46
|
+
rb_iv_set(self, "@proginfo", dtraceprograminfo);
|
47
|
+
}
|
48
|
+
|
49
|
+
if (ret < 0)
|
50
|
+
rb_raise(eDTraceException, "%s", dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
|
51
|
+
|
52
|
+
return Qnil;
|
53
|
+
}
|
54
|
+
|
55
|
+
/*
|
56
|
+
* Return this program's DTraceProgramInfo object. Returns nil unless
|
57
|
+
* the program has been executed.
|
58
|
+
*/
|
59
|
+
VALUE dtraceprogram_info(VALUE self)
|
60
|
+
{
|
61
|
+
return rb_iv_get(self, "@proginfo");
|
62
|
+
}
|
Binary file
|
@@ -0,0 +1,70 @@
|
|
1
|
+
/* Ruby-DTrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
RUBY_EXTERN VALUE cDTraceProgramInfo;
|
8
|
+
|
9
|
+
static void free_proginfo(void *p);
|
10
|
+
|
11
|
+
/* :nodoc: */
|
12
|
+
VALUE dtraceprograminfo_init(dtrace_proginfo_t *proginfo)
|
13
|
+
{
|
14
|
+
VALUE self;
|
15
|
+
|
16
|
+
self = Data_Wrap_Struct(cDTraceProgramInfo, 0, free_proginfo, proginfo);
|
17
|
+
return self;
|
18
|
+
}
|
19
|
+
|
20
|
+
/*
|
21
|
+
* Returns the number of aggregates associated with this program.
|
22
|
+
*/
|
23
|
+
VALUE dtraceprograminfo_aggregates_count(VALUE self)
|
24
|
+
{
|
25
|
+
dtrace_proginfo_t *proginfo;
|
26
|
+
|
27
|
+
Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
|
28
|
+
return INT2NUM(proginfo->dpi_aggregates);
|
29
|
+
}
|
30
|
+
|
31
|
+
/*
|
32
|
+
* Returns the number of record generating probes associated with this
|
33
|
+
* program.
|
34
|
+
*/
|
35
|
+
VALUE dtraceprograminfo_recgens_count(VALUE self)
|
36
|
+
{
|
37
|
+
dtrace_proginfo_t *proginfo;
|
38
|
+
|
39
|
+
Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
|
40
|
+
return INT2NUM(proginfo->dpi_recgens);
|
41
|
+
}
|
42
|
+
|
43
|
+
/*
|
44
|
+
* Returns the number of probes matched by this program.
|
45
|
+
*/
|
46
|
+
VALUE dtraceprograminfo_matches_count(VALUE self)
|
47
|
+
{
|
48
|
+
dtrace_proginfo_t *proginfo;
|
49
|
+
|
50
|
+
Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
|
51
|
+
return INT2NUM(proginfo->dpi_matches);
|
52
|
+
}
|
53
|
+
|
54
|
+
/*
|
55
|
+
* Returns the number of speculations specified by this program.
|
56
|
+
*/
|
57
|
+
VALUE dtraceprograminfo_speculations_count(VALUE self)
|
58
|
+
{
|
59
|
+
dtrace_proginfo_t *proginfo;
|
60
|
+
|
61
|
+
Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
|
62
|
+
return INT2NUM(proginfo->dpi_speculations);
|
63
|
+
}
|
64
|
+
|
65
|
+
static void free_proginfo(void *p)
|
66
|
+
{
|
67
|
+
dtrace_proginfo_t *proginfo = p;
|
68
|
+
free(proginfo);
|
69
|
+
}
|
70
|
+
|