authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-19 12:42:32-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-10-19 12:42:32-04:00
log63dfca97159fd66563693039606c0bca30aa1f85
tree7975692d2f2934f19d13a1784db87619c525bcaf
parente42d86b657c2fae093fb2545e8b4b85614a0c906
parent4e985123447dba40e282e9663e3ca669d3f9243a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #3482 from ziglang/mem-usage-report

add -DZIG_ENABLE_MEM_PROFILE option and -fmem-report flag

13 files changed, 315 insertions(+), 83 deletions(-)

CMakeLists.txt+2
......@@ -46,6 +46,7 @@ message("Configuring zig version ${ZIG_VERSION}")
4646set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)")
4747set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")
4848set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix")
49set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation")
4950
5051if(ZIG_STATIC)
5152 set(ZIG_STATIC_LLVM "on")
......@@ -455,6 +456,7 @@ set(ZIG_SOURCES
455456 "${CMAKE_SOURCE_DIR}/src/ir_print.cpp"
456457 "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp"
457458 "${CMAKE_SOURCE_DIR}/src/link.cpp"
459 "${CMAKE_SOURCE_DIR}/src/memory_profiling.cpp"
458460 "${CMAKE_SOURCE_DIR}/src/os.cpp"
459461 "${CMAKE_SOURCE_DIR}/src/parser.cpp"
460462 "${CMAKE_SOURCE_DIR}/src/range_set.cpp"
src/all_types.hpp+3-3
......@@ -2569,12 +2569,12 @@ enum IrInstructionId {
25692569struct IrInstruction {
25702570 Scope *scope;
25712571 AstNode *source_node;
2572 ConstExprValue value;
2573 size_t debug_id;
25742572 LLVMValueRef llvm_value;
2573 ConstExprValue value;
2574 uint32_t debug_id;
25752575 // if ref_count is zero and the instruction has no side effects,
25762576 // the instruction can be omitted in codegen
2577 size_t ref_count;
2577 uint32_t ref_count;
25782578 // When analyzing IR, instructions that point to this instruction in the "old ir"
25792579 // can find the instruction that corresponds to this value in the "new ir"
25802580 // with this child field.
src/analyze.cpp+2-2
......@@ -5783,8 +5783,8 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_
57835783
57845784
57855785ConstExprValue *create_const_vals(size_t count) {
5786 ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count);
5787 ConstExprValue *vals = allocate<ConstExprValue>(count);
5786 ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count, "ConstGlobalRefs");
5787 ConstExprValue *vals = allocate<ConstExprValue>(count, "ConstExprValue");
57885788 for (size_t i = 0; i < count; i += 1) {
57895789 vals[i].global_refs = &global_refs[i];
57905790 }
src/config.h.in+2-3
......@@ -13,9 +13,6 @@
1313#define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@
1414#define ZIG_VERSION_STRING "@ZIG_VERSION@"
1515
16// Only used for running tests before installing.
17#define ZIG_TEST_DIR "@CMAKE_SOURCE_DIR@/test"
18
1916// Used for communicating build information to self hosted build.
2017#define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@"
2118#define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@"
......@@ -24,4 +21,6 @@
2421#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@"
2522#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"
2623
24#cmakedefine ZIG_ENABLE_MEM_PROFILE
25
2726#endif
src/dump_analysis.cpp+1-18
......@@ -240,23 +240,6 @@ static void jw_string(JsonWriter *jw, const char *s) {
240240
241241static void tree_print(FILE *f, ZigType *ty, size_t indent);
242242
243static void pretty_print_bytes(FILE *f, double n) {
244 if (n > 1024.0 * 1024.0 * 1024.0) {
245 fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0);
246 return;
247 }
248 if (n > 1024.0 * 1024.0) {
249 fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0);
250 return;
251 }
252 if (n > 1024.0) {
253 fprintf(f, "%.02f KiB", n / 1024.0);
254 return;
255 }
256 fprintf(f, "%.02f bytes", n );
257 return;
258}
259
260243static int compare_type_abi_sizes_desc(const void *a, const void *b) {
261244 uint64_t size_a = (*(ZigType * const*)(a))->abi_size;
262245 uint64_t size_b = (*(ZigType * const*)(b))->abi_size;
......@@ -322,7 +305,7 @@ static void tree_print(FILE *f, ZigType *ty, size_t indent) {
322305
323306 start_peer(f, indent);
324307 fprintf(f, "\"sizef\": \"");
325 pretty_print_bytes(f, ty->abi_size);
308 zig_pretty_print_bytes(f, ty->abi_size);
326309 fprintf(f, "\"");
327310
328311 start_peer(f, indent);
src/ir.cpp+35-24
......@@ -413,7 +413,7 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
413413}
414414
415415static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) {
416 IrBasicBlock *result = allocate<IrBasicBlock>(1);
416 IrBasicBlock *result = allocate<IrBasicBlock>(1, "IrBasicBlock");
417417 result->scope = scope;
418418 result->name_hint = name_hint;
419419 result->debug_id = exec_next_debug_id(irb->exec);
......@@ -1085,13 +1085,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) {
10851085
10861086template<typename T>
10871087static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
1088 T *special_instruction = allocate<T>(1);
1088 const char *name = nullptr;
1089#ifdef ZIG_ENABLE_MEM_PROFILE
1090 T *dummy = nullptr;
1091 name = ir_instruction_type_str(ir_instruction_id(dummy));
1092#endif
1093 T *special_instruction = allocate<T>(1, name);
10891094 special_instruction->base.id = ir_instruction_id(special_instruction);
10901095 special_instruction->base.scope = scope;
10911096 special_instruction->base.source_node = source_node;
10921097 special_instruction->base.debug_id = exec_next_debug_id(irb->exec);
10931098 special_instruction->base.owner_bb = irb->current_basic_block;
1094 special_instruction->base.value.global_refs = allocate<ConstGlobalRefs>(1);
1099 special_instruction->base.value.global_refs = allocate<ConstGlobalRefs>(1, "ConstGlobalRefs");
10951100 return special_instruction;
10961101}
10971102
......@@ -3569,7 +3574,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
35693574 switch (node->data.return_expr.kind) {
35703575 case ReturnKindUnconditional:
35713576 {
3572 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);
3577 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
35733578 result_loc_ret->base.id = ResultLocIdReturn;
35743579 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
35753580
......@@ -3664,7 +3669,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
36643669 ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));
36653670 IrInstructionSpillBegin *spill_begin = ir_build_spill_begin(irb, scope, node, err_val,
36663671 SpillIdRetErrCode);
3667 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);
3672 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
36683673 result_loc_ret->base.id = ResultLocIdReturn;
36693674 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
36703675 ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
......@@ -3692,7 +3697,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s
36923697 Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime,
36933698 bool skip_name_check)
36943699{
3695 ZigVar *variable_entry = allocate<ZigVar>(1);
3700 ZigVar *variable_entry = allocate<ZigVar>(1, "ZigVar");
36963701 variable_entry->parent_scope = parent_scope;
36973702 variable_entry->shadowable = is_shadowable;
36983703 variable_entry->mem_slot_index = SIZE_MAX;
......@@ -3767,7 +3772,7 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n
37673772}
37683773
37693774static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
3770 ResultLocPeer *result = allocate<ResultLocPeer>(1);
3775 ResultLocPeer *result = allocate<ResultLocPeer>(1, "ResultLocPeer");
37713776 result->base.id = ResultLocIdPeer;
37723777 result->base.source_instruction = peer_parent->base.source_instruction;
37733778 result->parent = peer_parent;
......@@ -3806,7 +3811,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
38063811 scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
38073812 ir_should_inline(irb->exec, parent_scope));
38083813
3809 scope_block->peer_parent = allocate<ResultLocPeerParent>(1);
3814 scope_block->peer_parent = allocate<ResultLocPeerParent>(1, "ResultLocPeerParent");
38103815 scope_block->peer_parent->base.id = ResultLocIdPeerParent;
38113816 scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
38123817 scope_block->peer_parent->end_bb = scope_block->end_block;
......@@ -3933,7 +3938,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node)
39333938 if (lvalue == irb->codegen->invalid_instruction)
39343939 return irb->codegen->invalid_instruction;
39353940
3936 ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
3941 ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1, "ResultLocInstruction");
39373942 result_loc_inst->base.id = ResultLocIdInstruction;
39383943 result_loc_inst->base.source_instruction = lvalue;
39393944 ir_ref_instruction(lvalue, irb->current_basic_block);
......@@ -4005,10 +4010,10 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node
40054010
40064011 ir_set_cursor_at_end_and_append_block(irb, true_block);
40074012
4008 IrInstruction **incoming_values = allocate<IrInstruction *>(2);
4013 IrInstruction **incoming_values = allocate<IrInstruction *>(2, "IrInstruction *");
40094014 incoming_values[0] = val1;
40104015 incoming_values[1] = val2;
4011 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
4016 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
40124017 incoming_blocks[0] = post_val1_block;
40134018 incoming_blocks[1] = post_val2_block;
40144019
......@@ -8017,7 +8022,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
80178022 err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
80188023 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
80198024
8020 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
8025 size_t errors_count = irb->codegen->errors_by_index.length + err_count;
8026 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
80218027
80228028 for (uint32_t i = 0; i < err_count; i += 1) {
80238029 AstNode *field_node = node->data.err_set_decl.decls.at(i);
......@@ -8048,7 +8054,7 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
80488054 }
80498055 errors[err->value] = err;
80508056 }
8051 free(errors);
8057 deallocate(errors, errors_count, "ErrorTableEntry *");
80528058 return ir_build_const_type(irb, parent_scope, node, err_set_type);
80538059}
80548060
......@@ -9574,7 +9580,8 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
95749580 if (type_is_global_error_set(set2)) {
95759581 return set1;
95769582 }
9577 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
9583 size_t errors_count = ira->codegen->errors_by_index.length;
9584 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
95789585 populate_error_set_table(errors, set1);
95799586 ZigList<ErrorTableEntry *> intersection_list = {};
95809587
......@@ -9595,7 +9602,7 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
95959602 buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name));
95969603 }
95979604 }
9598 free(errors);
9605 deallocate(errors, errors_count, "ErrorTableEntry *");
95999606
96009607 err_set_type->data.error_set.err_count = intersection_list.length;
96019608 err_set_type->data.error_set.errors = intersection_list.items;
......@@ -9792,7 +9799,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
97929799 return result;
97939800 }
97949801
9795 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(g->errors_by_index.length);
9802 size_t errors_count = g->errors_by_index.length;
9803 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
97969804 for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
97979805 ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
97989806 assert(errors[error_entry->value] == nullptr);
......@@ -9809,7 +9817,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
98099817 result.data.error_set_mismatch->missing_errors.append(contained_error_entry);
98109818 }
98119819 }
9812 free(errors);
9820 deallocate(errors, errors_count, "ErrorTableEntry *");
98139821 return result;
98149822 }
98159823
......@@ -10814,7 +10822,8 @@ static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction
1081410822 IrSuspendPosition *suspend_pos)
1081510823{
1081610824 if (ira->codegen->verbose_ir) {
10817 fprintf(stderr, "suspend %s_%zu %s_%zu #%zu (%zu,%zu)\n", ira->old_irb.current_basic_block->name_hint,
10825 fprintf(stderr, "suspend %s_%zu %s_%zu #%" PRIu32 " (%zu,%zu)\n",
10826 ira->old_irb.current_basic_block->name_hint,
1081810827 ira->old_irb.current_basic_block->debug_id,
1081910828 ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint,
1082010829 ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id,
......@@ -10852,7 +10861,7 @@ static IrInstruction *ira_resume(IrAnalyze *ira) {
1085210861 ira->instruction_index = pos.instruction_index;
1085310862 assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length);
1085410863 if (ira->codegen->verbose_ir) {
10855 fprintf(stderr, "%s_%zu #%zu\n", ira->old_irb.current_basic_block->name_hint,
10864 fprintf(stderr, "%s_%zu #%" PRIu32 "\n", ira->old_irb.current_basic_block->name_hint,
1085610865 ira->old_irb.current_basic_block->debug_id,
1085710866 ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id);
1085810867 }
......@@ -14686,14 +14695,15 @@ static IrInstruction *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira,
1468614695 return ira->codegen->invalid_instruction;
1468714696 }
1468814697
14689 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
14698 size_t errors_count = ira->codegen->errors_by_index.length;
14699 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
1469014700 for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
1469114701 ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
1469214702 assert(errors[error_entry->value] == nullptr);
1469314703 errors[error_entry->value] = error_entry;
1469414704 }
1469514705 ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name);
14696 free(errors);
14706 deallocate(errors, errors_count, "ErrorTableEntry *");
1469714707
1469814708 return ir_const_type(ira, &instruction->base, result_type);
1469914709}
......@@ -24034,7 +24044,8 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2403424044 return ira->codegen->invalid_instruction;
2403524045 }
2403624046
24037 AstNode **field_prev_uses = allocate<AstNode *>(ira->codegen->errors_by_index.length);
24047 size_t field_prev_uses_count = ira->codegen->errors_by_index.length;
24048 AstNode **field_prev_uses = allocate<AstNode *>(field_prev_uses_count, "AstNode *");
2403824049
2403924050 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
2404024051 IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
......@@ -24091,7 +24102,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2409124102 }
2409224103 }
2409324104
24094 free(field_prev_uses);
24105 deallocate(field_prev_uses, field_prev_uses_count, "AstNode *");
2409524106 } else if (switch_type->id == ZigTypeIdInt) {
2409624107 RangeSet rs = {0};
2409724108 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
......@@ -26318,7 +26329,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_
2631826329 }
2631926330
2632026331 if (ira->codegen->verbose_ir) {
26321 fprintf(stderr, "analyze #%zu\n", old_instruction->debug_id);
26332 fprintf(stderr, "analyze #%" PRIu32 "\n", old_instruction->debug_id);
2632226333 }
2632326334 IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction);
2632426335 if (new_instruction != nullptr) {
src/ir_print.cpp+6-6
......@@ -38,8 +38,8 @@ struct IrPrint {
3838
3939static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction);
4040
41static const char* ir_instruction_type_str(IrInstruction* instruction) {
42 switch (instruction->id) {
41const char* ir_instruction_type_str(IrInstructionId id) {
42 switch (id) {
4343 case IrInstructionIdInvalid:
4444 return "Invalid";
4545 case IrInstructionIdShuffleVector:
......@@ -385,9 +385,9 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction, bool trail
385385 const char mark = trailing ? ':' : '#';
386386 const char *type_name = instruction->value.type ? buf_ptr(&instruction->value.type->name) : "(unknown)";
387387 const char *ref_count = ir_has_side_effects(instruction) ?
388 "-" : buf_ptr(buf_sprintf("%" ZIG_PRI_usize "", instruction->ref_count));
389 fprintf(irp->f, "%c%-3zu| %-22s| %-12s| %-2s| ", mark, instruction->debug_id,
390 ir_instruction_type_str(instruction), type_name, ref_count);
388 "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->ref_count));
389 fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->debug_id,
390 ir_instruction_type_str(instruction->id), type_name, ref_count);
391391}
392392
393393static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) {
......@@ -398,7 +398,7 @@ static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) {
398398}
399399
400400static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) {
401 fprintf(irp->f, "#%" ZIG_PRI_usize "", instruction->debug_id);
401 fprintf(irp->f, "#%" PRIu32 "", instruction->debug_id);
402402 if (irp->pass != IrPassSrc && irp->printed.maybe_get(instruction) == nullptr) {
403403 irp->printed.put(instruction, 0);
404404 irp->pending.append(instruction);
src/ir_print.hpp+2
......@@ -15,4 +15,6 @@
1515void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, IrPass pass);
1616void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, IrPass pass);
1717
18const char* ir_instruction_type_str(IrInstructionId id);
19
1820#endif
src/main.cpp+52-23
......@@ -64,6 +64,9 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
6464 " -fno-PIC disable Position Independent Code\n"
6565 " -ftime-report print timing diagnostics\n"
6666 " -fstack-report print stack size diagnostics\n"
67#ifdef ZIG_ENABLE_MEM_PROFILE
68 " -fmem-report print memory usage diagnostics\n"
69#endif
6770 " -fdump-analysis write analysis.json file with type information\n"
6871 " -femit-docs create a docs/ dir with html documentation\n"
6972 " -fno-emit-bin skip emitting machine code\n"
......@@ -306,9 +309,29 @@ static int zig_error_no_build_file(void) {
306309
307310extern "C" int ZigClang_main(int argc, char **argv);
308311
312#ifdef ZIG_ENABLE_MEM_PROFILE
313bool mem_report = false;
314#endif
315
316int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) {
317 if (root_progress_node != nullptr) {
318 stage2_progress_end(root_progress_node);
319 }
320#ifdef ZIG_ENABLE_MEM_PROFILE
321 if (mem_report) {
322 memprof_dump_stats(stderr);
323 }
324#endif
325 return exit_code;
326}
327
309328int main(int argc, char **argv) {
310329 stage2_attach_segfault_handler();
311330
331#ifdef ZIG_ENABLE_MEM_PROFILE
332 memprof_init();
333#endif
334
312335 char *arg0 = argv[0];
313336 Error err;
314337
......@@ -670,6 +693,13 @@ int main(int argc, char **argv) {
670693 timing_info = true;
671694 } else if (strcmp(arg, "-fstack-report") == 0) {
672695 stack_report = true;
696 } else if (strcmp(arg, "-fmem-report") == 0) {
697#ifdef ZIG_ENABLE_MEM_PROFILE
698 mem_report = true;
699#else
700 fprintf(stderr, "-fmem-report requires configuring with -DZIG_ENABLE_MEM_PROFILE=ON\n");
701 return print_error_usage(arg0);
702#endif
673703 } else if (strcmp(arg, "-fdump-analysis") == 0) {
674704 enable_dump_analysis = true;
675705 } else if (strcmp(arg, "-femit-docs") == 0) {
......@@ -1038,16 +1068,14 @@ int main(int argc, char **argv) {
10381068 if (in_file) {
10391069 ZigLibCInstallation libc;
10401070 if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true)))
1041 return EXIT_FAILURE;
1042 stage2_progress_end(root_progress_node);
1043 return EXIT_SUCCESS;
1071 return main_exit(root_progress_node, EXIT_FAILURE);
1072 return main_exit(root_progress_node, EXIT_SUCCESS);
10441073 }
10451074 ZigLibCInstallation libc;
10461075 if ((err = zig_libc_find_native(&libc, true)))
1047 return EXIT_FAILURE;
1076 return main_exit(root_progress_node, EXIT_FAILURE);
10481077 zig_libc_render(&libc, stdout);
1049 stage2_progress_end(root_progress_node);
1050 return EXIT_SUCCESS;
1078 return main_exit(root_progress_node, EXIT_SUCCESS);
10511079 }
10521080 case CmdBuiltin: {
10531081 CodeGen *g = codegen_create(main_pkg_path, nullptr, &target,
......@@ -1065,10 +1093,9 @@ int main(int argc, char **argv) {
10651093 Buf *builtin_source = codegen_generate_builtin_source(g);
10661094 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
10671095 fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
1068 return EXIT_FAILURE;
1096 return main_exit(root_progress_node, EXIT_FAILURE);
10691097 }
1070 stage2_progress_end(root_progress_node);
1071 return EXIT_SUCCESS;
1098 return main_exit(root_progress_node, EXIT_SUCCESS);
10721099 }
10731100 case CmdRun:
10741101 case CmdBuild:
......@@ -1142,7 +1169,7 @@ int main(int argc, char **argv) {
11421169 libc = allocate<ZigLibCInstallation>(1);
11431170 if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) {
11441171 fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err));
1145 return EXIT_FAILURE;
1172 return main_exit(root_progress_node, EXIT_FAILURE);
11461173 }
11471174 }
11481175 Buf *cache_dir_buf;
......@@ -1219,7 +1246,7 @@ int main(int argc, char **argv) {
12191246 codegen_set_rdynamic(g, rdynamic);
12201247 if (mmacosx_version_min && mios_version_min) {
12211248 fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");
1222 return EXIT_FAILURE;
1249 return main_exit(root_progress_node, EXIT_FAILURE);
12231250 }
12241251
12251252 if (mmacosx_version_min) {
......@@ -1259,6 +1286,11 @@ int main(int argc, char **argv) {
12591286 zig_print_stack_report(g, stdout);
12601287
12611288 if (cmd == CmdRun) {
1289 stage2_progress_end(root_progress_node);
1290#ifdef ZIG_ENABLE_MEM_PROFILE
1291 memprof_dump_stats(stderr);
1292#endif
1293
12621294 const char *exec_path = buf_ptr(&g->output_file_path);
12631295 ZigList<const char*> args = {0};
12641296
......@@ -1282,10 +1314,9 @@ int main(int argc, char **argv) {
12821314 buf_replace(&g->output_file_path, '/', '\\');
12831315#endif
12841316 if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0)
1285 return EXIT_FAILURE;
1317 return main_exit(root_progress_node, EXIT_FAILURE);
12861318 }
1287 stage2_progress_end(root_progress_node);
1288 return EXIT_SUCCESS;
1319 return main_exit(root_progress_node, EXIT_SUCCESS);
12891320 } else {
12901321 zig_unreachable();
12911322 }
......@@ -1293,8 +1324,7 @@ int main(int argc, char **argv) {
12931324 codegen_translate_c(g, in_file_buf, stdout, cmd == CmdTranslateCUserland);
12941325 if (timing_info)
12951326 codegen_print_timing_report(g, stderr);
1296 stage2_progress_end(root_progress_node);
1297 return EXIT_SUCCESS;
1327 return main_exit(root_progress_node, EXIT_SUCCESS);
12981328 } else if (cmd == CmdTest) {
12991329 codegen_set_emit_file_type(g, emit_file_type);
13001330
......@@ -1314,7 +1344,7 @@ int main(int argc, char **argv) {
13141344
13151345 if (g->disable_bin_generation) {
13161346 fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n");
1317 return 0;
1347 return main_exit(root_progress_node, EXIT_SUCCESS);
13181348 }
13191349
13201350 Buf *test_exe_path_unresolved = &g->output_file_path;
......@@ -1324,7 +1354,7 @@ int main(int argc, char **argv) {
13241354 if (emit_file_type != EmitFileTypeBinary) {
13251355 fprintf(stderr, "Created %s but skipping execution because it is non executable.\n",
13261356 buf_ptr(test_exe_path));
1327 return 0;
1357 return main_exit(root_progress_node, EXIT_SUCCESS);
13281358 }
13291359
13301360 for (size_t i = 0; i < test_exec_args.length; i += 1) {
......@@ -1336,7 +1366,7 @@ int main(int argc, char **argv) {
13361366 if (!target_can_exec(&native, &target) && test_exec_args.length == 0) {
13371367 fprintf(stderr, "Created %s but skipping execution because it is non-native.\n",
13381368 buf_ptr(test_exe_path));
1339 return 0;
1369 return main_exit(root_progress_node, EXIT_SUCCESS);
13401370 }
13411371
13421372 Termination term;
......@@ -1348,21 +1378,20 @@ int main(int argc, char **argv) {
13481378 fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
13491379 fprintf(stderr, "%s\n", buf_ptr(test_exe_path));
13501380 }
1351 stage2_progress_end(root_progress_node);
1352 return (term.how == TerminationIdClean) ? term.code : -1;
1381 return main_exit(root_progress_node, (term.how == TerminationIdClean) ? term.code : -1);
13531382 } else {
13541383 zig_unreachable();
13551384 }
13561385 }
13571386 case CmdVersion:
13581387 printf("%s\n", ZIG_VERSION_STRING);
1359 return EXIT_SUCCESS;
1388 return main_exit(root_progress_node, EXIT_SUCCESS);
13601389 case CmdZen: {
13611390 const char *ptr;
13621391 size_t len;
13631392 stage2_zen(&ptr, &len);
13641393 fwrite(ptr, len, 1, stdout);
1365 return EXIT_SUCCESS;
1394 return main_exit(root_progress_node, EXIT_SUCCESS);
13661395 }
13671396 case CmdTargets:
13681397 return print_target_list(stdout);
src/memory_profiling.cpp created+139
......@@ -0,0 +1,139 @@
1#include "memory_profiling.hpp"
2#include "hash_map.hpp"
3#include "list.hpp"
4#include "util.hpp"
5#include <string.h>
6
7#ifdef ZIG_ENABLE_MEM_PROFILE
8
9static bool str_eql_str(const char *a, const char *b) {
10 return strcmp(a, b) == 0;
11}
12
13static uint32_t str_hash(const char *s) {
14 // FNV 32-bit hash
15 uint32_t h = 2166136261;
16 for (; *s; s += 1) {
17 h = h ^ *s;
18 h = h * 16777619;
19 }
20 return h;
21}
22
23struct CountAndSize {
24 size_t item_count;
25 size_t type_size;
26};
27
28ZigList<const char *> unknown_names = {};
29HashMap<const char *, CountAndSize, str_hash, str_eql_str> usage_table = {};
30bool table_active = false;
31
32
33static const char *get_default_name(const char *name_or_null, size_t type_size) {
34 if (name_or_null != nullptr) return name_or_null;
35 if (type_size >= unknown_names.length) {
36 table_active = false;
37 unknown_names.resize(type_size + 1);
38 table_active = true;
39 }
40 if (unknown_names.at(type_size) == nullptr) {
41 char buf[100];
42 sprintf(buf, "Unknown_%zu%c", type_size, 0);
43 unknown_names.at(type_size) = strdup(buf);
44 }
45 return unknown_names.at(type_size);
46}
47
48void memprof_alloc(const char *name, size_t count, size_t type_size) {
49 if (!table_active) return;
50 if (count == 0) return;
51 // temporarily disable during table put
52 table_active = false;
53 name = get_default_name(name, type_size);
54 auto existing_entry = usage_table.put_unique(name, {count, type_size});
55 if (existing_entry != nullptr) {
56 assert(existing_entry->value.type_size == type_size); // allocated name does not match type
57 existing_entry->value.item_count += count;
58 }
59 table_active = true;
60}
61
62void memprof_dealloc(const char *name, size_t count, size_t type_size) {
63 if (!table_active) return;
64 if (count == 0) return;
65 name = get_default_name(name, type_size);
66 auto existing_entry = usage_table.maybe_get(name);
67 if (existing_entry == nullptr) {
68 zig_panic("deallocated more than allocated; compromised memory usage stats");
69 }
70 if (existing_entry->value.type_size != type_size) {
71 zig_panic("deallocated name '%s' does not match expected type size %zu", name, type_size);
72 }
73 existing_entry->value.item_count -= count;
74}
75
76void memprof_init(void) {
77 usage_table.init(1024);
78 table_active = true;
79}
80
81struct MemItem {
82 const char *type_name;
83 CountAndSize count_and_size;
84};
85
86static size_t get_bytes(const MemItem *item) {
87 return item->count_and_size.item_count * item->count_and_size.type_size;
88}
89
90static int compare_bytes_desc(const void *a, const void *b) {
91 size_t size_a = get_bytes((const MemItem *)(a));
92 size_t size_b = get_bytes((const MemItem *)(b));
93 if (size_a > size_b)
94 return -1;
95 if (size_a < size_b)
96 return 1;
97 return 0;
98}
99
100void memprof_dump_stats(FILE *file) {
101 assert(table_active);
102 // disable modifications from this function
103 table_active = false;
104
105 ZigList<MemItem> list = {};
106
107 auto it = usage_table.entry_iterator();
108 for (;;) {
109 auto *entry = it.next();
110 if (!entry)
111 break;
112
113 list.append({entry->key, entry->value});
114 }
115
116 qsort(list.items, list.length, sizeof(MemItem), compare_bytes_desc);
117
118 size_t total_bytes_used = 0;
119
120 for (size_t i = 0; i < list.length; i += 1) {
121 const MemItem *item = &list.at(i);
122 fprintf(file, "%s: %zu items, %zu bytes each, total ", item->type_name,
123 item->count_and_size.item_count, item->count_and_size.type_size);
124 size_t bytes = get_bytes(item);
125 zig_pretty_print_bytes(file, bytes);
126 fprintf(file, "\n");
127
128 total_bytes_used += bytes;
129 }
130
131 fprintf(stderr, "Total bytes used: ");
132 zig_pretty_print_bytes(file, total_bytes_used);
133 fprintf(file, "\n");
134
135 list.deinit();
136 table_active = true;
137}
138
139#endif
src/memory_profiling.hpp created+22
......@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2019 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ZIG_MEMORY_PROFILING_HPP
9#define ZIG_MEMORY_PROFILING_HPP
10
11#include "config.h"
12
13#include <stddef.h>
14#include <stdio.h>
15
16void memprof_init(void);
17
18void memprof_alloc(const char *name, size_t item_count, size_t type_size);
19void memprof_dealloc(const char *name, size_t item_count, size_t type_size);
20
21void memprof_dump_stats(FILE *file);
22#endif
src/util.cpp+18
......@@ -119,3 +119,21 @@ Slice<uint8_t> SplitIterator_rest(SplitIterator *self) {
119119SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) {
120120 return SplitIterator{0, buffer, split_bytes};
121121}
122
123void zig_pretty_print_bytes(FILE *f, double n) {
124 if (n > 1024.0 * 1024.0 * 1024.0) {
125 fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0);
126 return;
127 }
128 if (n > 1024.0 * 1024.0) {
129 fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0);
130 return;
131 }
132 if (n > 1024.0) {
133 fprintf(f, "%.02f KiB", n / 1024.0);
134 return;
135 }
136 fprintf(f, "%.02f bytes", n );
137 return;
138}
139
src/util.hpp+31-4
......@@ -8,6 +8,8 @@
88#ifndef ZIG_UTIL_HPP
99#define ZIG_UTIL_HPP
1010
11#include "memory_profiling.hpp"
12
1113#include <stdlib.h>
1214#include <stdint.h>
1315#include <string.h>
......@@ -96,7 +98,10 @@ static inline int ctzll(unsigned long long mask) {
9698
9799
98100template<typename T>
99ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) {
101ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count, const char *name = nullptr) {
102#ifdef ZIG_ENABLE_MEM_PROFILE
103 memprof_alloc(name, count, sizeof(T));
104#endif
100105#ifndef NDEBUG
101106 // make behavior when size == 0 portable
102107 if (count == 0)
......@@ -109,7 +114,10 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) {
109114}
110115
111116template<typename T>
112ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) {
117ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count, const char *name = nullptr) {
118#ifdef ZIG_ENABLE_MEM_PROFILE
119 memprof_alloc(name, count, sizeof(T));
120#endif
113121#ifndef NDEBUG
114122 // make behavior when size == 0 portable
115123 if (count == 0)
......@@ -122,7 +130,7 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) {
122130}
123131
124132template<typename T>
125static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
133static inline T *reallocate(T *old, size_t old_count, size_t new_count, const char *name = nullptr) {
126134 T *ptr = reallocate_nonzero(old, old_count, new_count);
127135 if (new_count > old_count) {
128136 memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T));
......@@ -131,7 +139,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
131139}
132140
133141template<typename T>
134static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {
142static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count, const char *name = nullptr) {
143#ifdef ZIG_ENABLE_MEM_PROFILE
144 memprof_dealloc(name, old_count, sizeof(T));
145 memprof_alloc(name, new_count, sizeof(T));
146#endif
135147#ifndef NDEBUG
136148 // make behavior when size == 0 portable
137149 if (new_count == 0 && old == nullptr)
......@@ -143,6 +155,19 @@ static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count)
143155 return ptr;
144156}
145157
158template<typename T>
159static inline void deallocate(T *old, size_t count, const char *name = nullptr) {
160#ifdef ZIG_ENABLE_MEM_PROFILE
161 memprof_dealloc(name, count, sizeof(T));
162#endif
163 free(old);
164}
165
166template<typename T>
167static inline void destroy(T *old, const char *name = nullptr) {
168 return deallocate(old, 1);
169}
170
146171template <typename T, size_t n>
147172constexpr size_t array_length(const T (&)[n]) {
148173 return n;
......@@ -225,6 +250,8 @@ static inline double zig_f16_to_double(float16_t x) {
225250 return z;
226251}
227252
253void zig_pretty_print_bytes(FILE *f, double n);
254
228255template<typename T>
229256struct Optional {
230257 T value;