mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 06:38:47 +00:00
Add frontend stuff instrumentation tooling
//+no-instrumentation @(no_instrumentation) @(instrumentation_enter) @(instrumentation_exit)
This commit is contained in:
@@ -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.entry_point_only = ac.entry_point_only;
|
||||||
e->Procedure.is_export = ac.is_export;
|
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->deprecated_message = ac.deprecated_message;
|
||||||
e->warning_message = ac.warning_message;
|
e->warning_message = ac.warning_message;
|
||||||
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
|
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
|
||||||
|
|||||||
@@ -2581,6 +2581,9 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
|||||||
str_lit("multi_pointer_slice_expr_error"),
|
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);
|
generate_minimum_dependency_set_internal(c, start);
|
||||||
|
|
||||||
|
|
||||||
@@ -3414,8 +3417,38 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (name == "entry_point_only") {
|
} else if (name == "entry_point_only") {
|
||||||
|
if (value != nullptr) {
|
||||||
|
error(value, "'%.*s' expects no parameter", LIT(name));
|
||||||
|
}
|
||||||
ac->entry_point_only = true;
|
ac->entry_point_only = true;
|
||||||
return 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;
|
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.entity_queue.count.load(std::memory_order_relaxed) == 0);
|
||||||
GB_ASSERT(c->info.definition_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");
|
TIME_SECTION("add untyped expression values");
|
||||||
// Add untyped expression values
|
// Add untyped expression values
|
||||||
for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) {
|
for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) {
|
||||||
|
|||||||
+24
-11
@@ -103,6 +103,12 @@ struct DeferredProcedure {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum InstrumentationFlag : i32 {
|
||||||
|
Instrumentation_Enabled = -1,
|
||||||
|
Instrumentation_Default = 0,
|
||||||
|
Instrumentation_Disabled = +1,
|
||||||
|
};
|
||||||
|
|
||||||
struct AttributeContext {
|
struct AttributeContext {
|
||||||
String link_name;
|
String link_name;
|
||||||
String link_prefix;
|
String link_prefix;
|
||||||
@@ -113,20 +119,23 @@ struct AttributeContext {
|
|||||||
String deprecated_message;
|
String deprecated_message;
|
||||||
String warning_message;
|
String warning_message;
|
||||||
DeferredProcedure deferred_procedure;
|
DeferredProcedure deferred_procedure;
|
||||||
bool is_export : 1;
|
bool is_export : 1;
|
||||||
bool is_static : 1;
|
bool is_static : 1;
|
||||||
bool require_results : 1;
|
bool require_results : 1;
|
||||||
bool require_declaration : 1;
|
bool require_declaration : 1;
|
||||||
bool has_disabled_proc : 1;
|
bool has_disabled_proc : 1;
|
||||||
bool disabled_proc : 1;
|
bool disabled_proc : 1;
|
||||||
bool test : 1;
|
bool test : 1;
|
||||||
bool init : 1;
|
bool init : 1;
|
||||||
bool fini : 1;
|
bool fini : 1;
|
||||||
bool set_cold : 1;
|
bool set_cold : 1;
|
||||||
bool entry_point_only : 1;
|
bool entry_point_only : 1;
|
||||||
|
bool instrumentation_enter : 1;
|
||||||
|
bool instrumentation_exit : 1;
|
||||||
u32 optimization_mode; // ProcedureOptimizationMode
|
u32 optimization_mode; // ProcedureOptimizationMode
|
||||||
i64 foreign_import_priority_index;
|
i64 foreign_import_priority_index;
|
||||||
String extra_linker_flags;
|
String extra_linker_flags;
|
||||||
|
InstrumentationFlag no_instrumentation;
|
||||||
|
|
||||||
String objc_class;
|
String objc_class;
|
||||||
String objc_name;
|
String objc_name;
|
||||||
@@ -403,6 +412,10 @@ struct CheckerInfo {
|
|||||||
|
|
||||||
BlockingMutex all_procedures_mutex;
|
BlockingMutex all_procedures_mutex;
|
||||||
Array<ProcInfo *> all_procedures;
|
Array<ProcInfo *> all_procedures;
|
||||||
|
|
||||||
|
BlockingMutex instrumentation_mutex;
|
||||||
|
Entity *instrumentation_enter_entity;
|
||||||
|
Entity *instrumentation_exit_entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CheckerContext {
|
struct CheckerContext {
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ struct Entity {
|
|||||||
bool generated_from_polymorphic : 1;
|
bool generated_from_polymorphic : 1;
|
||||||
bool target_feature_disabled : 1;
|
bool target_feature_disabled : 1;
|
||||||
bool entry_point_only : 1;
|
bool entry_point_only : 1;
|
||||||
|
bool no_instrumentation : 1;
|
||||||
String target_feature;
|
String target_feature;
|
||||||
} Procedure;
|
} Procedure;
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
+3
-1
@@ -5919,7 +5919,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
|
|||||||
f->vet_flags = parse_vet_tag(tok, lc);
|
f->vet_flags = parse_vet_tag(tok, lc);
|
||||||
f->vet_flags_set = true;
|
f->vet_flags_set = true;
|
||||||
} else if (string_starts_with(lc, str_lit("+ignore"))) {
|
} else if (string_starts_with(lc, str_lit("+ignore"))) {
|
||||||
return false;
|
return false;
|
||||||
} else if (string_starts_with(lc, str_lit("+private"))) {
|
} else if (string_starts_with(lc, str_lit("+private"))) {
|
||||||
f->flags |= AstFile_IsPrivatePkg;
|
f->flags |= AstFile_IsPrivatePkg;
|
||||||
String command = string_trim_starts_with(lc, str_lit("+private "));
|
String command = string_trim_starts_with(lc, str_lit("+private "));
|
||||||
@@ -5941,6 +5941,8 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
|
|||||||
} else {
|
} else {
|
||||||
f->flags |= AstFile_IsLazy;
|
f->flags |= AstFile_IsLazy;
|
||||||
}
|
}
|
||||||
|
} else if (lc == "+no-instrumentation") {
|
||||||
|
f->flags |= AstFile_NoInstrumentation;
|
||||||
} else {
|
} else {
|
||||||
warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc));
|
warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ enum AstFileFlag : u32 {
|
|||||||
|
|
||||||
AstFile_IsTest = 1<<3,
|
AstFile_IsTest = 1<<3,
|
||||||
AstFile_IsLazy = 1<<4,
|
AstFile_IsLazy = 1<<4,
|
||||||
|
|
||||||
|
AstFile_NoInstrumentation = 1<<5,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum AstDelayQueueKind {
|
enum AstDelayQueueKind {
|
||||||
|
|||||||
Reference in New Issue
Block a user