11const std = @import ("std" );
22const sig = @import ("sig.zig" );
3+ const cli = @import ("cli" );
34
45const accountsdb_fuzz = sig .accounts_db .fuzz ;
5- const gossip_fuzz_service = sig .gossip .fuzz_service ;
6- const gossip_fuzz_table = sig .gossip .fuzz_table ;
7- const accountsdb_snapshot_fuzz = sig .accounts_db .fuzz_snapshot ;
6+ const gossip_service_fuzz = sig .gossip .fuzz_service ;
7+ const gossip_table_fuzz = sig .gossip .fuzz_table ;
8+ const snapshot_fuzz = sig .accounts_db .fuzz_snapshot ;
89const ledger_fuzz = sig .ledger .fuzz_ledger ;
9- const ChannelPrintLogger = sig .trace .ChannelPrintLogger ;
10- const Level = sig .trace .Level ;
11-
12- const servePrometheus = sig .prometheus .servePrometheus ;
13- const globalRegistry = sig .prometheus .globalRegistry ;
14-
15- // where seeds are saved (in case of too many logs)
16- const SEED_FILE_PATH = sig .TEST_DATA_DIR ++ "fuzz_seeds.txt" ;
1710
1811// Supported fuzz filters.
1912// NOTE: changing these enum variants will require a change to the fuzz/kcov in `scripts/`
@@ -22,85 +15,160 @@ pub const FuzzFilter = enum {
2215 snapshot ,
2316 gossip_service ,
2417 gossip_table ,
25- allocators ,
2618 ledger ,
19+ allocators ,
20+ };
21+
22+ const Cmd = struct {
23+ data_dir : ? []const u8 ,
24+ seed : ? u64 ,
25+ fuzzer : ? union (FuzzFilter ) {
26+ accountsdb : accountsdb_fuzz.RunCmd ,
27+ snapshot : FuzzerTodo ,
28+ gossip_service : FuzzerTodo ,
29+ gossip_table : FuzzerTodo ,
30+ ledger : FuzzerTodo ,
31+ allocators : FuzzerTodo ,
32+ },
33+
34+ const FuzzerTodo = struct {
35+ args : []const []const u8 ,
36+
37+ pub const cmd_info : cli .CommandInfo (FuzzerTodo ) = .{
38+ .help = .{
39+ .short = "TODO: implement bespoke CLI integration for this fuzzer." ,
40+ .long = null ,
41+ },
42+ .sub = .{
43+ .args = .{
44+ .kind = .positional ,
45+ .name_override = null ,
46+ .alias = .none ,
47+ .default_value = &.{},
48+ .config = .string ,
49+ .help = "Args to pass to the specified fuzzer." ,
50+ },
51+ },
52+ };
53+ };
54+
55+ const parser = cli .Parser (Cmd , .{
56+ .help = .{
57+ .short = "Fuzz a component of the validator." ,
58+ .long = null ,
59+ },
60+ .sub = .{
61+ .data_dir = .{
62+ .kind = .named ,
63+ .name_override = null ,
64+ .alias = .none ,
65+ .default_value = null ,
66+ .config = .string ,
67+ .help = "Directory for all fuzzers to store their on-disk data relative to." ,
68+ },
69+ .seed = .{
70+ .kind = .named ,
71+ .name_override = null ,
72+ .alias = .none ,
73+ .default_value = null ,
74+ .config = {},
75+ .help = "Seed for the PRNG for all random actions taken during fuzzing." ,
76+ },
77+ .fuzzer = .{
78+ .accountsdb = accountsdb_fuzz .RunCmd .cmd_info ,
79+ .snapshot = FuzzerTodo .cmd_info ,
80+ .gossip_service = FuzzerTodo .cmd_info ,
81+ .gossip_table = FuzzerTodo .cmd_info ,
82+ .ledger = FuzzerTodo .cmd_info ,
83+ .allocators = FuzzerTodo .cmd_info ,
84+ },
85+ },
86+ });
2787};
2888
2989pub fn main () ! void {
3090 var gpa_state : std .heap .DebugAllocator (.{}) = .init ;
3191 defer _ = gpa_state .deinit ();
32- const allocator = gpa_state .allocator ();
92+ const gpa = gpa_state .allocator ();
93+
94+ const argv = try std .process .argsAlloc (gpa );
95+ defer std .process .argsFree (gpa , argv );
96+ const args = argv [1.. ];
97+
98+ const stderr = std .io .getStdErr ();
99+ const stderr_tty = std .io .tty .detectConfig (stderr );
100+ const cmd : Cmd = cmd : {
101+ std .debug .lockStdErr ();
102+ defer std .debug .unlockStdErr ();
103+ break :cmd try Cmd .parser .parse (
104+ gpa ,
105+ "fuzz" ,
106+ stderr_tty ,
107+ stderr .writer (),
108+ args ,
109+ ) orelse return ;
110+ };
111+ defer Cmd .parser .free (gpa , cmd );
33112
34- var std_logger = try ChannelPrintLogger .init (.{
35- .allocator = std . heap . c_allocator ,
36- .max_level = Level .debug ,
113+ const std_logger : * sig.trace.ChannelPrintLogger = try .init (.{
114+ .allocator = gpa ,
115+ .max_level = .debug ,
37116 .max_buffer = 1 << 20 ,
38117 }, null );
39118 defer std_logger .deinit ();
40-
41119 const logger = std_logger .logger ("fuzz" );
42120
43- var cli_args = try std .process .argsWithAllocator (allocator );
44- defer cli_args .deinit ();
45-
46- const metrics_port : u16 = 12345 ;
47-
48- logger .info ().logf ("metrics port: {d}" , .{metrics_port });
49- const metrics_thread = try std .Thread
50- // TODO: use the GPA here, the server is just leaking because we're losing the handle
51- // to it and never deiniting.
52- .spawn (.{}, servePrometheus , .{ std .heap .c_allocator , globalRegistry (), 12355 });
53- metrics_thread .detach ();
54-
55- _ = cli_args .skip ();
56- const filter = blk : {
57- const maybe_filter = cli_args .next ();
58- if (maybe_filter ) | filter | {
59- const parsed_filter = std .meta .stringToEnum (FuzzFilter , filter ) orelse {
60- std .debug .print (
61- "Unknown filter. Supported values are: {s} " ,
62- .{std .meta .fieldNames (FuzzFilter )},
63- );
64- return error .UnknownFilter ;
65- };
66- std .debug .print ("filtering fuzz testing with prefix: {s}\n " , .{filter });
67- break :blk parsed_filter ;
68- } else {
69- std .debug .print ("fuzz filter required: usage: zig build fuzz -- <filter>\n " , .{});
70- return error .NoFilterProvided ;
71- }
72- };
73-
74- const seed = blk : {
75- const maybe_seed = cli_args .next ();
76- if (maybe_seed ) | seed_str | {
77- break :blk try std .fmt .parseInt (u64 , seed_str , 10 );
78- } else {
79- break :blk std .crypto .random .int (u64 );
80- }
121+ const data_dir_name = cmd .data_dir orelse sig .FUZZ_DATA_DIR ;
122+ const seed = cmd .seed orelse std .crypto .random .int (u64 );
123+ const fuzzer = cmd .fuzzer orelse {
124+ std .debug .print ("Missing filter.\n " , .{});
125+ return ;
81126 };
82127
83- std .debug .print ("using seed: {d}\n " , .{seed });
84- try writeSeedToFile (filter , seed );
85-
86- switch (filter ) {
87- .accountsdb = > try accountsdb_fuzz .run (seed , & cli_args ),
88- .snapshot = > try accountsdb_snapshot_fuzz .run (& cli_args ),
89- .gossip_service = > try gossip_fuzz_service .run (seed , & cli_args ),
90- .gossip_table = > try gossip_fuzz_table .run (seed , & cli_args ),
91- .ledger = > try ledger_fuzz .run (seed , & cli_args ),
92- .allocators = > try sig .utils .allocators .runFuzzer (seed , & cli_args ),
128+ var data_dir = try std .fs .cwd ().makeOpenPath (data_dir_name , .{});
129+ defer data_dir .close ();
130+
131+ {
132+ std .debug .print ("using seed: {d}\n " , .{seed });
133+ // where seeds are saved (in case of too many logs)
134+ const seed_file = try data_dir .createFile ("fuzz_seeds.txt" , .{ .truncate = false });
135+ defer seed_file .close ();
136+ try seed_file .seekFromEnd (0 );
137+ const now : u64 = @intCast (std .time .timestamp ());
138+ try seed_file .writer ().print (
139+ "{s}: time: {d}, seed: {d}\n " ,
140+ .{ @tagName (fuzzer ), now , seed },
141+ );
93142 }
94- }
95143
96- /// writes the seed to the defined seed file (defined by SEED_FILE_PATH)
97- pub fn writeSeedToFile (filter : FuzzFilter , seed : u64 ) ! void {
98- const seed_file = try std .fs .cwd ().createFile (SEED_FILE_PATH , .{
99- .truncate = false ,
144+ const metrics_port : u16 = 12345 ;
145+ logger .info ().logf ("metrics port: {d}" , .{metrics_port });
146+ // TODO: use the GPA here, the server is just leaking because we're losing the handle
147+ // to it and never deiniting.
148+ const metrics_thread : std.Thread = try .spawn (.{}, sig .prometheus .servePrometheus , .{
149+ std .heap .c_allocator ,
150+ sig .prometheus .globalRegistry (),
151+ metrics_port ,
100152 });
101- defer seed_file .close ();
102- try seed_file .seekFromEnd (0 );
153+ metrics_thread .detach ();
103154
104- const now : u64 = @intCast (std .time .timestamp ());
105- try seed_file .writer ().print ("{s}: time: {d}, seed: {d}\n " , .{ @tagName (filter ), now , seed });
155+ var sub_data_dir = try data_dir .makeOpenPath (@tagName (fuzzer ), .{});
156+ defer sub_data_dir .close ();
157+
158+ switch (fuzzer ) {
159+ .accountsdb ,
160+ = > | run_cmd | try accountsdb_fuzz .run (
161+ gpa ,
162+ .from (logger ),
163+ seed ,
164+ sub_data_dir ,
165+ run_cmd ,
166+ ),
167+
168+ .snapshot = > try snapshot_fuzz .run (),
169+ .gossip_service = > | run_cmd | try gossip_service_fuzz .run (seed , run_cmd .args ),
170+ .gossip_table = > | run_cmd | try gossip_table_fuzz .run (seed , run_cmd .args ),
171+ .ledger = > | run_cmd | try ledger_fuzz .run (seed , run_cmd .args ),
172+ .allocators = > | run_cmd | try sig .utils .allocators .runFuzzer (seed , run_cmd .args ),
173+ }
106174}
0 commit comments