Skip to content

Commit bc3cee8

Browse files
committed
More instrumentation (memory allocation, also fixes #7259)
1 parent 69b210f commit bc3cee8

File tree

8 files changed

+180
-7
lines changed

8 files changed

+180
-7
lines changed

base/exports.jl

+1
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,7 @@ export
10701070
gc_disable,
10711071
gc_enable,
10721072
precompile,
1073+
clear_malloc_data,
10731074

10741075
# misc
10751076
atexit,

base/util.jl

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ gc_time_ns() = ccall(:jl_gc_total_hrtime, Uint64, ())
1212
# total number of bytes allocated so far
1313
gc_bytes() = ccall(:jl_gc_total_bytes, Int64, ())
1414

15+
# reset the malloc log. Used to avoid counting memory allocated during compilation.
16+
clear_malloc_data() = ccall(:jl_clear_malloc_data, Void, ())
17+
1518
function tic()
1619
t0 = time_ns()
1720
task_local_storage(:TIMERS, (t0, get(task_local_storage(), :TIMERS, ())))

src/codegen.cpp

+26-4
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ static Function *jlgetnthfieldchecked_func;
321321
#ifdef _OS_WINDOWS_
322322
static Function *resetstkoflw_func;
323323
#endif
324+
static Function *diff_gc_total_bytes_func;
325+
static Function *show_execution_point_func;
324326

325327
// --- code generation ---
326328

@@ -3366,8 +3368,9 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle)
33663368
ctx.f = f;
33673369

33683370
// step 5. set up debug info context and create first basic block
3369-
bool do_coverage =
3370-
jl_compileropts.code_coverage && lam->module != jl_base_module && lam->module != jl_core_module;
3371+
bool in_user_code = lam->module != jl_base_module && lam->module != jl_core_module;
3372+
bool do_coverage = jl_compileropts.code_coverage == JL_LOG_ALL || (jl_compileropts.code_coverage == JL_LOG_USER && in_user_code);
3373+
bool do_malloc_log = jl_compileropts.malloc_log == JL_LOG_ALL || (jl_compileropts.malloc_log == JL_LOG_USER && in_user_code);
33713374
jl_value_t *stmt = jl_cellref(stmts,0);
33723375
std::string filename = "no file";
33733376
char *dbgFuncName = lam->name->name;
@@ -3413,6 +3416,7 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle)
34133416
builder.SetCurrentDebugLocation(noDbg);
34143417
debug_enabled = false;
34153418
do_coverage = false;
3419+
do_malloc_log = false;
34163420
}
34173421
else {
34183422
// TODO: Fix when moving to new LLVM version
@@ -3717,16 +3721,17 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle)
37173721
bool prevlabel = false;
37183722
for(i=0; i < stmtslen; i++) {
37193723
jl_value_t *stmt = jl_cellref(stmts,i);
3724+
int lno = -1;
37203725
if (jl_is_linenode(stmt)) {
3721-
int lno = jl_linenode_line(stmt);
3726+
lno = jl_linenode_line(stmt);
37223727
if (debug_enabled)
37233728
builder.SetCurrentDebugLocation(DebugLoc::get(lno, 1, (MDNode*)SP, NULL));
37243729
if (do_coverage)
37253730
coverageVisitLine(filename, lno);
37263731
ctx.lineno = lno;
37273732
}
37283733
else if (jl_is_expr(stmt) && ((jl_expr_t*)stmt)->head == line_sym) {
3729-
int lno = jl_unbox_long(jl_exprarg(stmt, 0));
3734+
lno = jl_unbox_long(jl_exprarg(stmt, 0));
37303735
if (debug_enabled)
37313736
builder.SetCurrentDebugLocation(DebugLoc::get(lno, 1, (MDNode*)SP, NULL));
37323737
if (do_coverage)
@@ -3775,6 +3780,8 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle)
37753780
else {
37763781
(void)emit_expr(stmt, &ctx, false, false);
37773782
}
3783+
if (do_malloc_log && lno != -1)
3784+
mallocVisitLine(filename, lno);
37783785
}
37793786
// sometimes we have dangling labels after the end
37803787
if (builder.GetInsertBlock()->getTerminator() == NULL) {
@@ -4253,6 +4260,21 @@ static void init_julia_llvm_env(Module *m)
42534260
"jl_get_nth_field_checked", m);
42544261
add_named_global(jlgetnthfieldchecked_func, (void*)*jl_get_nth_field_checked);
42554262

4263+
diff_gc_total_bytes_func =
4264+
Function::Create(FunctionType::get(T_int64, false),
4265+
Function::ExternalLinkage,
4266+
"diff_gc_total_bytes", m);
4267+
add_named_global(diff_gc_total_bytes_func, (void*)*diff_gc_total_bytes);
4268+
4269+
std::vector<Type *> execpoint_args(0);
4270+
execpoint_args.push_back(T_pint8);
4271+
execpoint_args.push_back(T_int32);
4272+
show_execution_point_func =
4273+
Function::Create(FunctionType::get(T_void, execpoint_args, false),
4274+
Function::ExternalLinkage,
4275+
"show_execution_point", m);
4276+
add_named_global(show_execution_point_func, (void*)*show_execution_point);
4277+
42564278
// set up optimization passes
42574279
FPM = new FunctionPassManager(m);
42584280

src/debuginfo.cpp

+91
Original file line numberDiff line numberDiff line change
@@ -602,3 +602,94 @@ extern "C" void jl_write_coverage_data(void)
602602
}
603603
}
604604
}
605+
606+
// Memory allocation log (malloc_log)
607+
608+
typedef std::map<std::string,std::vector<GlobalVariable*> > mallocdata_t;
609+
static mallocdata_t mallocData;
610+
611+
static void mallocVisitLine(std::string filename, int line)
612+
{
613+
if (filename == "" || filename == "none" || filename == "no file") {
614+
sync_gc_total_bytes();
615+
return;
616+
}
617+
mallocdata_t::iterator it = mallocData.find(filename);
618+
if (it == mallocData.end()) {
619+
mallocData[filename] = std::vector<GlobalVariable*>(0);
620+
}
621+
std::vector<GlobalVariable*> &vec = mallocData[filename];
622+
if (vec.size() <= (size_t)line)
623+
vec.resize(line+1, NULL);
624+
if (vec[line] == NULL)
625+
vec[line] = new GlobalVariable(*jl_Module, T_int64, false,
626+
GlobalVariable::InternalLinkage,
627+
ConstantInt::get(T_int64,0), "bytecnt");
628+
GlobalVariable *v = vec[line];
629+
//builder.CreateCall2(prepare_call(show_execution_point_func), builder.CreateGlobalStringPtr(filename), ConstantInt::get(T_int32, line));
630+
builder.CreateStore(builder.CreateAdd(builder.CreateLoad(v),
631+
builder.CreateCall(prepare_call(diff_gc_total_bytes_func))),
632+
v);
633+
}
634+
635+
// Resets the malloc counts. Needed to avoid including memory usage
636+
// from JITting.
637+
extern "C" DLLEXPORT void jl_clear_malloc_data(void)
638+
{
639+
mallocdata_t::iterator it = mallocData.begin();
640+
for (; it != mallocData.end(); it++) {
641+
std::vector<GlobalVariable*> &bytes = (*it).second;
642+
std::vector<GlobalVariable*>::iterator itb;
643+
for (itb = bytes.begin(); itb != bytes.end(); itb++) {
644+
if (*itb) {
645+
int64_t *p = (int64_t*) jl_ExecutionEngine->getPointerToGlobal(*itb);
646+
*p = 0;
647+
}
648+
}
649+
}
650+
sync_gc_total_bytes();
651+
}
652+
653+
extern "C" void jl_write_malloc_log(void)
654+
{
655+
mallocdata_t::iterator it = mallocData.begin();
656+
for (; it != mallocData.end(); it++) {
657+
std::string filename = (*it).first;
658+
std::string outfile = filename + ".mlc";
659+
std::vector<GlobalVariable*> &bytes = (*it).second;
660+
if (bytes.size() > 1) {
661+
std::ifstream inf(filename.c_str());
662+
if (inf.is_open()) {
663+
std::ofstream outf(outfile.c_str(), std::ofstream::trunc | std::ofstream::out);
664+
char line[1024];
665+
int l = 1;
666+
while (!inf.eof()) {
667+
inf.getline(line, sizeof(line));
668+
int nbytes = -1;
669+
if ((size_t)l < bytes.size()) {
670+
GlobalVariable *gv = bytes[l];
671+
if (gv) {
672+
int *p = (int*)jl_ExecutionEngine->getPointerToGlobal(gv);
673+
nbytes = *p;
674+
}
675+
}
676+
outf.width(9);
677+
if (nbytes == -1)
678+
outf<<'-';
679+
else
680+
outf<<nbytes;
681+
outf.width(0);
682+
outf<<" "<<line<<std::endl;
683+
l++;
684+
}
685+
outf.close();
686+
inf.close();
687+
}
688+
}
689+
}
690+
}
691+
692+
void show_execution_point(char *filename, int lno)
693+
{
694+
jl_printf(JL_STDOUT, "executing file %s, line %d\n", filename, lno);
695+
}

src/gc.c

+10
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ typedef struct _bigval_t {
7272
// GC knobs and self-measurement variables
7373
static size_t allocd_bytes = 0;
7474
static int64_t total_allocd_bytes = 0;
75+
static int64_t last_gc_total_bytes = 0;
7576
static size_t freed_bytes = 0;
7677
static uint64_t total_gc_time=0;
7778
#ifdef _P64
@@ -907,6 +908,15 @@ DLLEXPORT int jl_gc_is_enabled(void) { return is_gc_enabled; }
907908
DLLEXPORT int64_t jl_gc_total_bytes(void) { return total_allocd_bytes + allocd_bytes; }
908909
DLLEXPORT uint64_t jl_gc_total_hrtime(void) { return total_gc_time; }
909910

911+
int64_t diff_gc_total_bytes(void)
912+
{
913+
int64_t oldtb = last_gc_total_bytes;
914+
int64_t newtb = jl_gc_total_bytes();
915+
last_gc_total_bytes = newtb;
916+
return newtb - oldtb;
917+
}
918+
void sync_gc_total_bytes(void) {last_gc_total_bytes = jl_gc_total_bytes();}
919+
910920
void jl_gc_ephemeral_on(void) { pools = &ephe_pools[0]; }
911921
void jl_gc_ephemeral_off(void) { pools = &norm_pools[0]; }
912922

src/init.c

+4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ extern BOOL (WINAPI *hSymRefreshModuleList)(HANDLE);
6767
char *julia_home = NULL;
6868
jl_compileropts_t jl_compileropts = { NULL, // build_path
6969
0, // code_coverage
70+
0, // malloc_log
7071
JL_COMPILEROPT_CHECK_BOUNDS_DEFAULT,
7172
0 // int32_literals
7273
};
@@ -378,6 +379,7 @@ static void jl_uv_exitcleanup_walk(uv_handle_t* handle, void *arg)
378379
}
379380

380381
void jl_write_coverage_data(void);
382+
void jl_write_malloc_log(void);
381383

382384
DLLEXPORT void uv_atexit_hook()
383385
{
@@ -386,6 +388,8 @@ DLLEXPORT void uv_atexit_hook()
386388
#endif
387389
if (jl_compileropts.code_coverage)
388390
jl_write_coverage_data();
391+
if (jl_compileropts.malloc_log)
392+
jl_write_malloc_log();
389393
if (jl_base_module) {
390394
jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_atexit"));
391395
if (f!=NULL && jl_is_function(f)) {

src/julia.h

+13
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,8 @@ DLLEXPORT void jl_gc_disable(void);
10521052
DLLEXPORT int jl_gc_is_enabled(void);
10531053
DLLEXPORT int64_t jl_gc_total_bytes(void);
10541054
DLLEXPORT uint64_t jl_gc_total_hrtime(void);
1055+
int64_t diff_gc_total_bytes(void);
1056+
void sync_gc_total_bytes(void);
10551057
void jl_gc_ephemeral_on(void);
10561058
void jl_gc_ephemeral_off(void);
10571059
DLLEXPORT void jl_gc_collect(void);
@@ -1071,6 +1073,8 @@ DLLEXPORT void *alloc_4w(void);
10711073
void *allocb(size_t sz);
10721074
DLLEXPORT void *allocobj(size_t sz);
10731075

1076+
DLLEXPORT void jl_clear_malloc_data(void);
1077+
10741078
#else
10751079

10761080
#define JL_GC_PUSH(...) ;
@@ -1306,17 +1310,26 @@ DLLEXPORT size_t jl_static_show(JL_STREAM *out, jl_value_t *v);
13061310
void jl_print_gc_stats(JL_STREAM *s);
13071311
#endif
13081312

1313+
// debugging
1314+
void show_execution_point(char *filename, int lno);
1315+
13091316
// compiler options -----------------------------------------------------------
13101317

13111318
typedef struct {
13121319
char *build_path;
13131320
int8_t code_coverage;
1321+
int8_t malloc_log;
13141322
int8_t check_bounds;
13151323
int int_literals;
13161324
} jl_compileropts_t;
13171325

13181326
extern DLLEXPORT jl_compileropts_t jl_compileropts;
13191327

1328+
// Settings for code_coverage and mallog_log
1329+
#define JL_LOG_NONE 0
1330+
#define JL_LOG_USER 1
1331+
#define JL_LOG_ALL 2
1332+
13201333
#define JL_COMPILEROPT_CHECK_BOUNDS_DEFAULT 0
13211334
#define JL_COMPILEROPT_CHECK_BOUNDS_ON 1
13221335
#define JL_COMPILEROPT_CHECK_BOUNDS_OFF 2

ui/repl.c

+32-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ extern DLLEXPORT char *julia_home;
4848
char system_image[256] = JL_SYSTEM_IMAGE_PATH;
4949

5050
static int lisp_prompt = 0;
51-
static int codecov=0;
51+
static int codecov = JL_LOG_NONE;
52+
static int malloclog= JL_LOG_NONE;
5253
static char *program = NULL;
5354
char *image_file = NULL;
5455

@@ -74,7 +75,10 @@ static const char *opts =
7475
" -F Load ~/.juliarc.jl, then handle remaining inputs\n"
7576
" --color={yes|no} Enable or disable color text\n\n"
7677

77-
" --code-coverage Count executions of source lines\n"
78+
" --code-coverage={none|user|all}, --code-coverage\n"
79+
" Count executions of source lines (omitting setting is equivalent to 'user')\n"
80+
" --track-allocation={none|user|all}\n"
81+
" Count bytes allocated by each source line\n"
7882
" --check-bounds={yes|no} Emit bounds checks always or never (ignoring declarations)\n"
7983
" --int-literals={32|64} Select integer literal size independent of platform\n";
8084

@@ -88,7 +92,8 @@ void parse_opts(int *argcp, char ***argvp)
8892
{ "lisp", no_argument, &lisp_prompt, 1 },
8993
{ "help", no_argument, 0, 'h' },
9094
{ "sysimage", required_argument, 0, 'J' },
91-
{ "code-coverage", no_argument, &codecov, 1 },
95+
{ "code-coverage", optional_argument, 0, 'c' },
96+
{ "track-allocation",required_argument, 0, 'm' },
9297
{ "check-bounds", required_argument, 0, 300 },
9398
{ "int-literals", required_argument, 0, 301 },
9499
{ 0, 0, 0, 0 }
@@ -122,6 +127,29 @@ void parse_opts(int *argcp, char ***argvp)
122127
case 'h':
123128
printf("%s%s", usage, opts);
124129
exit(0);
130+
case 'c':
131+
if (optarg != NULL) {
132+
if (!strcmp(optarg,"user"))
133+
codecov = JL_LOG_USER;
134+
else if (!strcmp(optarg,"all"))
135+
codecov = JL_LOG_ALL;
136+
else if (!strcmp(optarg,"none"))
137+
codecov = JL_LOG_NONE;
138+
break;
139+
}
140+
else
141+
codecov = JL_LOG_USER;
142+
break;
143+
case 'm':
144+
if (optarg != NULL) {
145+
if (!strcmp(optarg,"user"))
146+
malloclog = JL_LOG_USER;
147+
else if (!strcmp(optarg,"all"))
148+
malloclog = JL_LOG_ALL;
149+
else if (!strcmp(optarg,"none"))
150+
malloclog = JL_LOG_NONE;
151+
break;
152+
}
125153
case 300:
126154
if (!strcmp(optarg,"yes"))
127155
jl_compileropts.check_bounds = JL_COMPILEROPT_CHECK_BOUNDS_ON;
@@ -145,6 +173,7 @@ void parse_opts(int *argcp, char ***argvp)
145173
}
146174
}
147175
jl_compileropts.code_coverage = codecov;
176+
jl_compileropts.malloc_log = malloclog;
148177
if (!julia_home) {
149178
julia_home = getenv("JULIA_HOME");
150179
if (julia_home) {

0 commit comments

Comments
 (0)