浏览代码

Add frontend stuff instrumentation tooling

//+no-instrumentation
@(no_instrumentation)
@(instrumentation_enter)
@(instrumentation_exit)
gingerBill 1 年之前
父节点
当前提交
aff8f06e3c
共有 6 个文件被更改,包括 140 次插入12 次删除
  1. 66 0
      src/check_decl.cpp
  2. 44 0
      src/checker.cpp
  3. 24 11
      src/checker.hpp
  4. 1 0
      src/entity.cpp
  5. 3 1
      src/parser.cpp
  6. 2 0
      src/parser.hpp

+ 66 - 0
src/check_decl.cpp

@@ -909,6 +909,72 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 
 	e->Procedure.entry_point_only = ac.entry_point_only;
 	e->Procedure.is_export = ac.is_export;
+
+	bool no_instrumentation = false;
+	if (pl->body == nullptr) {
+		no_instrumentation = true;
+		if (ac.no_instrumentation != Instrumentation_Default) {
+			error(e->token, "@(no_instrumentation) is not allowed on foreign procedures");
+		}
+	} else {
+		if (e->file) {
+			no_instrumentation = (e->file->flags & AstFile_NoInstrumentation) != 0;
+		}
+
+		switch (ac.no_instrumentation) {
+		case Instrumentation_Enabled:  no_instrumentation = false; break;
+		case Instrumentation_Default:  break;
+		case Instrumentation_Disabled: no_instrumentation = true;  break;
+		}
+	}
+	e->Procedure.no_instrumentation = no_instrumentation;
+
+	auto const is_valid_instrumentation_call = [](Type *type) -> bool {
+		if (type == nullptr || type->kind != Type_Proc) {
+			return false;
+		}
+		if (type->Proc.calling_convention != ProcCC_CDecl) {
+			return false;
+		}
+		if (type->Proc.result_count != 0) {
+			return false;
+		}
+		if (type->Proc.param_count != 2) {
+			return false;
+		}
+		Type *p0 = type->Proc.params->Tuple.variables[0]->type;
+		Type *p1 = type->Proc.params->Tuple.variables[1]->type;
+		return is_type_rawptr(p0) && is_type_rawptr(p1);
+	};
+
+	if (ac.instrumentation_enter && ac.instrumentation_exit) {
+		error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)");
+	} else if (ac.instrumentation_enter) {
+		if (!is_valid_instrumentation_call(e->type)) {
+			gbString s = type_to_string(e->type);
+			error(e->token, "@(instrumentation_enter) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s);
+			gb_string_free(s);
+		}
+		MUTEX_GUARD(&ctx->info->instrumentation_mutex);
+		if (ctx->info->instrumentation_enter_entity != nullptr) {
+			error(e->token, "@(instrumentation_enter) has already been set");
+		} else {
+			ctx->info->instrumentation_enter_entity = e;
+		}
+	} else if (ac.instrumentation_exit) {
+		if (!is_valid_instrumentation_call(e->type)) {
+			gbString s = type_to_string(e->type);
+			error(e->token, "@(instrumentation_exit) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s);
+			gb_string_free(s);
+		}
+		MUTEX_GUARD(&ctx->info->instrumentation_mutex);
+		if (ctx->info->instrumentation_exit_entity != nullptr) {
+			error(e->token, "@(instrumentation_exit) has already been set");
+		} else {
+			ctx->info->instrumentation_exit_entity = e;
+		}
+	}
+
 	e->deprecated_message = ac.deprecated_message;
 	e->warning_message = ac.warning_message;
 	ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);

+ 44 - 0
src/checker.cpp

@@ -2581,6 +2581,9 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("multi_pointer_slice_expr_error"),
 	);
 
+	add_dependency_to_set(c, c->info.instrumentation_enter_entity);
+	add_dependency_to_set(c, c->info.instrumentation_exit_entity);
+
 	generate_minimum_dependency_set_internal(c, start);
 
 
@@ -3414,8 +3417,38 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		}
 		return true;
 	} else if (name == "entry_point_only") {
+		if (value != nullptr) {
+			error(value, "'%.*s' expects no parameter", LIT(name));
+		}
 		ac->entry_point_only = true;
 		return true;
+	} else if (name == "no_instrumentation") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_Invalid) {
+			ac->no_instrumentation = Instrumentation_Disabled;
+		} else if (ev.kind == ExactValue_Bool) {
+			if (ev.value_bool) {
+				ac->no_instrumentation = Instrumentation_Disabled;
+			} else {
+				ac->no_instrumentation = Instrumentation_Enabled;
+			}
+		} else {
+			error(value, "Expected either a boolean or no parameter for '%.*s'", LIT(name));
+			return false;
+		}
+		return true;
+	} else if (name == "instrumentation_enter") {
+		if (value != nullptr) {
+			error(value, "'%.*s' expects no parameter", LIT(name));
+		}
+		ac->instrumentation_enter = true;
+		return true;
+	} else if (name == "instrumentation_exit") {
+		if (value != nullptr) {
+			error(value, "'%.*s' expects no parameter", LIT(name));
+		}
+		ac->instrumentation_exit = true;
+		return true;
 	}
 	return false;
 }
@@ -6216,6 +6249,17 @@ gb_internal void check_parsed_files(Checker *c) {
 	GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0);
 	GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0);
 
+	TIME_SECTION("check instrumentation calls");
+	{
+		if ((c->info.instrumentation_enter_entity != nullptr) ^
+		    (c->info.instrumentation_exit_entity  != nullptr)) {
+			Entity *e = c->info.instrumentation_enter_entity;
+			if (!e) e = c->info.instrumentation_exit_entity;
+			error(e->token, "Both @(instrumentation_enter) and @(instrumentation_exit) must be defined");
+		}
+	}
+
+
 	TIME_SECTION("add untyped expression values");
 	// Add untyped expression values
 	for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) {

+ 24 - 11
src/checker.hpp

@@ -103,6 +103,12 @@ struct DeferredProcedure {
 };
 
 
+enum InstrumentationFlag : i32 {
+	Instrumentation_Enabled  = -1,
+	Instrumentation_Default  = 0,
+	Instrumentation_Disabled = +1,
+};
+
 struct AttributeContext {
 	String  link_name;
 	String  link_prefix;
@@ -113,20 +119,23 @@ struct AttributeContext {
 	String  deprecated_message;
 	String  warning_message;
 	DeferredProcedure deferred_procedure;
-	bool    is_export           : 1;
-	bool    is_static           : 1;
-	bool    require_results     : 1;
-	bool    require_declaration : 1;
-	bool    has_disabled_proc   : 1;
-	bool    disabled_proc       : 1;
-	bool    test                : 1;
-	bool    init                : 1;
-	bool    fini                : 1;
-	bool    set_cold            : 1;
-	bool    entry_point_only    : 1;
+	bool    is_export             : 1;
+	bool    is_static             : 1;
+	bool    require_results       : 1;
+	bool    require_declaration   : 1;
+	bool    has_disabled_proc     : 1;
+	bool    disabled_proc         : 1;
+	bool    test                  : 1;
+	bool    init                  : 1;
+	bool    fini                  : 1;
+	bool    set_cold              : 1;
+	bool    entry_point_only      : 1;
+	bool    instrumentation_enter : 1;
+	bool    instrumentation_exit  : 1;
 	u32 optimization_mode; // ProcedureOptimizationMode
 	i64 foreign_import_priority_index;
 	String extra_linker_flags;
+	InstrumentationFlag no_instrumentation;
 
 	String  objc_class;
 	String  objc_name;
@@ -403,6 +412,10 @@ struct CheckerInfo {
 
 	BlockingMutex all_procedures_mutex;
 	Array<ProcInfo *> all_procedures;
+
+	BlockingMutex instrumentation_mutex;
+	Entity *instrumentation_enter_entity;
+	Entity *instrumentation_exit_entity;
 };
 
 struct CheckerContext {

+ 1 - 0
src/entity.cpp

@@ -251,6 +251,7 @@ struct Entity {
 			bool    generated_from_polymorphic : 1;
 			bool    target_feature_disabled    : 1;
 			bool    entry_point_only           : 1;
+			bool    no_instrumentation         : 1;
 			String  target_feature;
 		} Procedure;
 		struct {

+ 3 - 1
src/parser.cpp

@@ -5919,7 +5919,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
 						f->vet_flags = parse_vet_tag(tok, lc);
 						f->vet_flags_set = true;
 					} else if (string_starts_with(lc, str_lit("+ignore"))) {
-							return false;
+						return false;
 					} else if (string_starts_with(lc, str_lit("+private"))) {
 						f->flags |= AstFile_IsPrivatePkg;
 						String command = string_trim_starts_with(lc, str_lit("+private "));
@@ -5941,6 +5941,8 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
 						} else {
 							f->flags |= AstFile_IsLazy;
 						}
+					} else if (lc == "+no-instrumentation") {
+						f->flags |= AstFile_NoInstrumentation;
 					} else {
 						warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc));
 					}

+ 2 - 0
src/parser.hpp

@@ -76,6 +76,8 @@ enum AstFileFlag : u32 {
 
 	AstFile_IsTest    = 1<<3,
 	AstFile_IsLazy    = 1<<4,
+
+	AstFile_NoInstrumentation = 1<<5,
 };
 
 enum AstDelayQueueKind {