1*387f9dfdSAndroid Build Coastguard Worker--[[ 2*387f9dfdSAndroid Build Coastguard WorkerCopyright 2016 Marek Vavrusa <[email protected]> 3*387f9dfdSAndroid Build Coastguard Worker 4*387f9dfdSAndroid Build Coastguard WorkerLicensed under the Apache License, Version 2.0 (the "License"); 5*387f9dfdSAndroid Build Coastguard Workeryou may not use this file except in compliance with the License. 6*387f9dfdSAndroid Build Coastguard WorkerYou may obtain a copy of the License at 7*387f9dfdSAndroid Build Coastguard Worker 8*387f9dfdSAndroid Build Coastguard Workerhttp://www.apache.org/licenses/LICENSE-2.0 9*387f9dfdSAndroid Build Coastguard Worker 10*387f9dfdSAndroid Build Coastguard WorkerUnless required by applicable law or agreed to in writing, software 11*387f9dfdSAndroid Build Coastguard Workerdistributed under the License is distributed on an "AS IS" BASIS, 12*387f9dfdSAndroid Build Coastguard WorkerWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*387f9dfdSAndroid Build Coastguard WorkerSee the License for the specific language governing permissions and 14*387f9dfdSAndroid Build Coastguard Workerlimitations under the License. 15*387f9dfdSAndroid Build Coastguard Worker]] 16*387f9dfdSAndroid Build Coastguard Workerlocal ffi = require('ffi') 17*387f9dfdSAndroid Build Coastguard Workerlocal bit = require('bit') 18*387f9dfdSAndroid Build Coastguard Workerlocal has_syscall, S = pcall(require, 'syscall') 19*387f9dfdSAndroid Build Coastguard Workerlocal M = {} 20*387f9dfdSAndroid Build Coastguard Worker 21*387f9dfdSAndroid Build Coastguard Workerffi.cdef [[ 22*387f9dfdSAndroid Build Coastguard Workerstruct bpf { 23*387f9dfdSAndroid Build Coastguard Worker /* Instruction classes */ 24*387f9dfdSAndroid Build Coastguard Worker static const int LD = 0x00; 25*387f9dfdSAndroid Build Coastguard Worker static const int LDX = 0x01; 26*387f9dfdSAndroid Build Coastguard Worker static const int ST = 0x02; 27*387f9dfdSAndroid Build Coastguard Worker static const int STX = 0x03; 28*387f9dfdSAndroid Build Coastguard Worker static const int ALU = 0x04; 29*387f9dfdSAndroid Build Coastguard Worker static const int JMP = 0x05; 30*387f9dfdSAndroid Build Coastguard Worker static const int ALU64 = 0x07; 31*387f9dfdSAndroid Build Coastguard Worker /* ld/ldx fields */ 32*387f9dfdSAndroid Build Coastguard Worker static const int W = 0x00; 33*387f9dfdSAndroid Build Coastguard Worker static const int H = 0x08; 34*387f9dfdSAndroid Build Coastguard Worker static const int B = 0x10; 35*387f9dfdSAndroid Build Coastguard Worker static const int ABS = 0x20; 36*387f9dfdSAndroid Build Coastguard Worker static const int IND = 0x40; 37*387f9dfdSAndroid Build Coastguard Worker static const int MEM = 0x60; 38*387f9dfdSAndroid Build Coastguard Worker static const int LEN = 0x80; 39*387f9dfdSAndroid Build Coastguard Worker static const int MSH = 0xa0; 40*387f9dfdSAndroid Build Coastguard Worker /* alu/jmp fields */ 41*387f9dfdSAndroid Build Coastguard Worker static const int ADD = 0x00; 42*387f9dfdSAndroid Build Coastguard Worker static const int SUB = 0x10; 43*387f9dfdSAndroid Build Coastguard Worker static const int MUL = 0x20; 44*387f9dfdSAndroid Build Coastguard Worker static const int DIV = 0x30; 45*387f9dfdSAndroid Build Coastguard Worker static const int OR = 0x40; 46*387f9dfdSAndroid Build Coastguard Worker static const int AND = 0x50; 47*387f9dfdSAndroid Build Coastguard Worker static const int LSH = 0x60; 48*387f9dfdSAndroid Build Coastguard Worker static const int RSH = 0x70; 49*387f9dfdSAndroid Build Coastguard Worker static const int NEG = 0x80; 50*387f9dfdSAndroid Build Coastguard Worker static const int MOD = 0x90; 51*387f9dfdSAndroid Build Coastguard Worker static const int XOR = 0xa0; 52*387f9dfdSAndroid Build Coastguard Worker static const int JA = 0x00; 53*387f9dfdSAndroid Build Coastguard Worker static const int JEQ = 0x10; 54*387f9dfdSAndroid Build Coastguard Worker static const int JGT = 0x20; 55*387f9dfdSAndroid Build Coastguard Worker static const int JGE = 0x30; 56*387f9dfdSAndroid Build Coastguard Worker static const int JSET = 0x40; 57*387f9dfdSAndroid Build Coastguard Worker static const int K = 0x00; 58*387f9dfdSAndroid Build Coastguard Worker static const int X = 0x08; 59*387f9dfdSAndroid Build Coastguard Worker static const int JNE = 0x50; /* jump != */ 60*387f9dfdSAndroid Build Coastguard Worker static const int JSGT = 0x60; /* SGT is signed '>', GT in x86 */ 61*387f9dfdSAndroid Build Coastguard Worker static const int JSGE = 0x70; /* SGE is signed '>=', GE in x86 */ 62*387f9dfdSAndroid Build Coastguard Worker static const int CALL = 0x80; /* function call */ 63*387f9dfdSAndroid Build Coastguard Worker static const int EXIT = 0x90; /* function return */ 64*387f9dfdSAndroid Build Coastguard Worker /* ld/ldx fields */ 65*387f9dfdSAndroid Build Coastguard Worker static const int DW = 0x18; /* double word */ 66*387f9dfdSAndroid Build Coastguard Worker static const int XADD = 0xc0; /* exclusive add */ 67*387f9dfdSAndroid Build Coastguard Worker /* alu/jmp fields */ 68*387f9dfdSAndroid Build Coastguard Worker static const int MOV = 0xb0; /* mov reg to reg */ 69*387f9dfdSAndroid Build Coastguard Worker static const int ARSH = 0xc0; /* sign extending arithmetic shift right */ 70*387f9dfdSAndroid Build Coastguard Worker /* change endianness of a register */ 71*387f9dfdSAndroid Build Coastguard Worker static const int END = 0xd0; /* flags for endianness conversion: */ 72*387f9dfdSAndroid Build Coastguard Worker static const int TO_LE = 0x00; /* convert to little-endian */ 73*387f9dfdSAndroid Build Coastguard Worker static const int TO_BE = 0x08; /* convert to big-endian */ 74*387f9dfdSAndroid Build Coastguard Worker /* misc */ 75*387f9dfdSAndroid Build Coastguard Worker static const int PSEUDO_MAP_FD = 0x01; 76*387f9dfdSAndroid Build Coastguard Worker /* helper functions */ 77*387f9dfdSAndroid Build Coastguard Worker static const int F_CURRENT_CPU = 0xffffffff; 78*387f9dfdSAndroid Build Coastguard Worker static const int F_USER_STACK = 1 << 8; 79*387f9dfdSAndroid Build Coastguard Worker static const int F_FAST_STACK_CMP = 1 << 9; 80*387f9dfdSAndroid Build Coastguard Worker static const int F_REUSE_STACKID = 1 << 10; 81*387f9dfdSAndroid Build Coastguard Worker /* special offsets for ancillary data */ 82*387f9dfdSAndroid Build Coastguard Worker static const int NET_OFF = -0x100000; 83*387f9dfdSAndroid Build Coastguard Worker static const int LL_OFF = -0x200000; 84*387f9dfdSAndroid Build Coastguard Worker}; 85*387f9dfdSAndroid Build Coastguard Worker/* eBPF commands */ 86*387f9dfdSAndroid Build Coastguard Workerstruct bpf_cmd { 87*387f9dfdSAndroid Build Coastguard Worker static const int MAP_CREATE = 0; 88*387f9dfdSAndroid Build Coastguard Worker static const int MAP_LOOKUP_ELEM = 1; 89*387f9dfdSAndroid Build Coastguard Worker static const int MAP_UPDATE_ELEM = 2; 90*387f9dfdSAndroid Build Coastguard Worker static const int MAP_DELETE_ELEM = 3; 91*387f9dfdSAndroid Build Coastguard Worker static const int MAP_GET_NEXT_KEY = 4; 92*387f9dfdSAndroid Build Coastguard Worker static const int PROG_LOAD = 5; 93*387f9dfdSAndroid Build Coastguard Worker static const int OBJ_PIN = 6; 94*387f9dfdSAndroid Build Coastguard Worker static const int OBJ_GET = 7; 95*387f9dfdSAndroid Build Coastguard Worker}; 96*387f9dfdSAndroid Build Coastguard Worker/* eBPF helpers */ 97*387f9dfdSAndroid Build Coastguard Workerstruct bpf_func_id { 98*387f9dfdSAndroid Build Coastguard Worker static const int unspec = 0; 99*387f9dfdSAndroid Build Coastguard Worker static const int map_lookup_elem = 1; 100*387f9dfdSAndroid Build Coastguard Worker static const int map_update_elem = 2; 101*387f9dfdSAndroid Build Coastguard Worker static const int map_delete_elem = 3; 102*387f9dfdSAndroid Build Coastguard Worker static const int probe_read = 4; 103*387f9dfdSAndroid Build Coastguard Worker static const int ktime_get_ns = 5; 104*387f9dfdSAndroid Build Coastguard Worker static const int trace_printk = 6; 105*387f9dfdSAndroid Build Coastguard Worker static const int get_prandom_u32 = 7; 106*387f9dfdSAndroid Build Coastguard Worker static const int get_smp_processor_id = 8; 107*387f9dfdSAndroid Build Coastguard Worker static const int skb_store_bytes = 9; 108*387f9dfdSAndroid Build Coastguard Worker static const int l3_csum_replace = 10; 109*387f9dfdSAndroid Build Coastguard Worker static const int l4_csum_replace = 11; 110*387f9dfdSAndroid Build Coastguard Worker static const int tail_call = 12; 111*387f9dfdSAndroid Build Coastguard Worker static const int clone_redirect = 13; 112*387f9dfdSAndroid Build Coastguard Worker static const int get_current_pid_tgid = 14; 113*387f9dfdSAndroid Build Coastguard Worker static const int get_current_uid_gid = 15; 114*387f9dfdSAndroid Build Coastguard Worker static const int get_current_comm = 16; 115*387f9dfdSAndroid Build Coastguard Worker static const int get_cgroup_classid = 17; 116*387f9dfdSAndroid Build Coastguard Worker static const int skb_vlan_push = 18; 117*387f9dfdSAndroid Build Coastguard Worker static const int skb_vlan_pop = 19; 118*387f9dfdSAndroid Build Coastguard Worker static const int skb_get_tunnel_key = 20; 119*387f9dfdSAndroid Build Coastguard Worker static const int skb_set_tunnel_key = 21; 120*387f9dfdSAndroid Build Coastguard Worker static const int perf_event_read = 22; 121*387f9dfdSAndroid Build Coastguard Worker static const int redirect = 23; 122*387f9dfdSAndroid Build Coastguard Worker static const int get_route_realm = 24; 123*387f9dfdSAndroid Build Coastguard Worker static const int perf_event_output = 25; 124*387f9dfdSAndroid Build Coastguard Worker static const int skb_load_bytes = 26; 125*387f9dfdSAndroid Build Coastguard Worker static const int get_stackid = 27; 126*387f9dfdSAndroid Build Coastguard Worker}; 127*387f9dfdSAndroid Build Coastguard Worker/* BPF_MAP_STACK_TRACE structures and constants */ 128*387f9dfdSAndroid Build Coastguard Workerstatic const int BPF_MAX_STACK_DEPTH = 127; 129*387f9dfdSAndroid Build Coastguard Workerstruct bpf_stacktrace { 130*387f9dfdSAndroid Build Coastguard Worker uint64_t ip[BPF_MAX_STACK_DEPTH]; 131*387f9dfdSAndroid Build Coastguard Worker}; 132*387f9dfdSAndroid Build Coastguard Worker]] 133*387f9dfdSAndroid Build Coastguard Worker 134*387f9dfdSAndroid Build Coastguard Worker-- Compatibility: ljsyscall doesn't have support for BPF syscall 135*387f9dfdSAndroid Build Coastguard Workerif not has_syscall or not S.bpf then 136*387f9dfdSAndroid Build Coastguard Worker error("ljsyscall doesn't support bpf(), must be updated") 137*387f9dfdSAndroid Build Coastguard Workerelse 138*387f9dfdSAndroid Build Coastguard Worker local strflag = require('syscall.helpers').strflag 139*387f9dfdSAndroid Build Coastguard Worker -- Compatibility: ljsyscall<=0.12 140*387f9dfdSAndroid Build Coastguard Worker if not S.c.BPF_MAP.LRU_HASH then 141*387f9dfdSAndroid Build Coastguard Worker S.c.BPF_MAP = strflag { 142*387f9dfdSAndroid Build Coastguard Worker UNSPEC = 0, 143*387f9dfdSAndroid Build Coastguard Worker HASH = 1, 144*387f9dfdSAndroid Build Coastguard Worker ARRAY = 2, 145*387f9dfdSAndroid Build Coastguard Worker PROG_ARRAY = 3, 146*387f9dfdSAndroid Build Coastguard Worker PERF_EVENT_ARRAY = 4, 147*387f9dfdSAndroid Build Coastguard Worker PERCPU_HASH = 5, 148*387f9dfdSAndroid Build Coastguard Worker PERCPU_ARRAY = 6, 149*387f9dfdSAndroid Build Coastguard Worker STACK_TRACE = 7, 150*387f9dfdSAndroid Build Coastguard Worker CGROUP_ARRAY = 8, 151*387f9dfdSAndroid Build Coastguard Worker LRU_HASH = 9, 152*387f9dfdSAndroid Build Coastguard Worker LRU_PERCPU_HASH = 10, 153*387f9dfdSAndroid Build Coastguard Worker LPM_TRIE = 11, 154*387f9dfdSAndroid Build Coastguard Worker ARRAY_OF_MAPS = 12, 155*387f9dfdSAndroid Build Coastguard Worker HASH_OF_MAPS = 13, 156*387f9dfdSAndroid Build Coastguard Worker DEVMAP = 14, 157*387f9dfdSAndroid Build Coastguard Worker SOCKMAP = 15, 158*387f9dfdSAndroid Build Coastguard Worker CPUMAP = 16, 159*387f9dfdSAndroid Build Coastguard Worker } 160*387f9dfdSAndroid Build Coastguard Worker end 161*387f9dfdSAndroid Build Coastguard Worker if not S.c.BPF_PROG.TRACEPOINT then 162*387f9dfdSAndroid Build Coastguard Worker S.c.BPF_PROG = strflag { 163*387f9dfdSAndroid Build Coastguard Worker UNSPEC = 0, 164*387f9dfdSAndroid Build Coastguard Worker SOCKET_FILTER = 1, 165*387f9dfdSAndroid Build Coastguard Worker KPROBE = 2, 166*387f9dfdSAndroid Build Coastguard Worker SCHED_CLS = 3, 167*387f9dfdSAndroid Build Coastguard Worker SCHED_ACT = 4, 168*387f9dfdSAndroid Build Coastguard Worker TRACEPOINT = 5, 169*387f9dfdSAndroid Build Coastguard Worker XDP = 6, 170*387f9dfdSAndroid Build Coastguard Worker PERF_EVENT = 7, 171*387f9dfdSAndroid Build Coastguard Worker CGROUP_SKB = 8, 172*387f9dfdSAndroid Build Coastguard Worker CGROUP_SOCK = 9, 173*387f9dfdSAndroid Build Coastguard Worker LWT_IN = 10, 174*387f9dfdSAndroid Build Coastguard Worker LWT_OUT = 11, 175*387f9dfdSAndroid Build Coastguard Worker LWT_XMIT = 12, 176*387f9dfdSAndroid Build Coastguard Worker SOCK_OPS = 13, 177*387f9dfdSAndroid Build Coastguard Worker SK_SKB = 14, 178*387f9dfdSAndroid Build Coastguard Worker CGROUP_DEVICE = 15, 179*387f9dfdSAndroid Build Coastguard Worker SK_MSG = 16, 180*387f9dfdSAndroid Build Coastguard Worker RAW_TRACEPOINT = 17, 181*387f9dfdSAndroid Build Coastguard Worker CGROUP_SOCK_ADDR = 18, 182*387f9dfdSAndroid Build Coastguard Worker } 183*387f9dfdSAndroid Build Coastguard Worker end 184*387f9dfdSAndroid Build Coastguard Workerend 185*387f9dfdSAndroid Build Coastguard Worker 186*387f9dfdSAndroid Build Coastguard Worker-- Compatibility: metatype for stacktrace 187*387f9dfdSAndroid Build Coastguard Workerlocal function stacktrace_iter(t, i) 188*387f9dfdSAndroid Build Coastguard Worker i = i + 1 189*387f9dfdSAndroid Build Coastguard Worker if i < #t and t.ip[i] > 0 then 190*387f9dfdSAndroid Build Coastguard Worker return i, t.ip[i] 191*387f9dfdSAndroid Build Coastguard Worker end 192*387f9dfdSAndroid Build Coastguard Workerend 193*387f9dfdSAndroid Build Coastguard Workerffi.metatype('struct bpf_stacktrace', { 194*387f9dfdSAndroid Build Coastguard Worker __len = function (t) return ffi.sizeof(t.ip) / ffi.sizeof(t.ip[0]) end, 195*387f9dfdSAndroid Build Coastguard Worker __ipairs = function (t) return stacktrace_iter, t, -1 end, 196*387f9dfdSAndroid Build Coastguard Worker}) 197*387f9dfdSAndroid Build Coastguard Worker 198*387f9dfdSAndroid Build Coastguard Worker-- Reflect cdata type 199*387f9dfdSAndroid Build Coastguard Workerfunction M.typename(v) 200*387f9dfdSAndroid Build Coastguard Worker if not v or type(v) ~= 'cdata' then return nil end 201*387f9dfdSAndroid Build Coastguard Worker return string.match(tostring(ffi.typeof(v)), '<([^>]+)') 202*387f9dfdSAndroid Build Coastguard Workerend 203*387f9dfdSAndroid Build Coastguard Worker 204*387f9dfdSAndroid Build Coastguard Worker-- Reflect if cdata type can be pointer (accepts array or pointer) 205*387f9dfdSAndroid Build Coastguard Workerfunction M.isptr(v, noarray) 206*387f9dfdSAndroid Build Coastguard Worker local ctname = M.typename(v) 207*387f9dfdSAndroid Build Coastguard Worker if ctname then 208*387f9dfdSAndroid Build Coastguard Worker ctname = string.sub(ctname, -1) 209*387f9dfdSAndroid Build Coastguard Worker ctname = ctname == '*' or (not noarray and ctname == ']') 210*387f9dfdSAndroid Build Coastguard Worker end 211*387f9dfdSAndroid Build Coastguard Worker return ctname 212*387f9dfdSAndroid Build Coastguard Workerend 213*387f9dfdSAndroid Build Coastguard Worker 214*387f9dfdSAndroid Build Coastguard Worker-- Return true if variable is a non-nil constant that can be used as immediate value 215*387f9dfdSAndroid Build Coastguard Worker-- e.g. result of KSHORT and KNUM 216*387f9dfdSAndroid Build Coastguard Workerfunction M.isimmconst(v) 217*387f9dfdSAndroid Build Coastguard Worker return (type(v.const) == 'number' and not ffi.istype(v.type, ffi.typeof('void'))) 218*387f9dfdSAndroid Build Coastguard Worker or type(v.const) == 'cdata' and ffi.istype(v.type, ffi.typeof('uint64_t')) -- Lua numbers are at most 52 bits 219*387f9dfdSAndroid Build Coastguard Worker or type(v.const) == 'cdata' and ffi.istype(v.type, ffi.typeof('int64_t')) 220*387f9dfdSAndroid Build Coastguard Workerend 221*387f9dfdSAndroid Build Coastguard Worker 222*387f9dfdSAndroid Build Coastguard Workerfunction M.osversion() 223*387f9dfdSAndroid Build Coastguard Worker -- We have no better way to extract current kernel hex-string other 224*387f9dfdSAndroid Build Coastguard Worker -- than parsing headers, compiling a helper function or reading /proc 225*387f9dfdSAndroid Build Coastguard Worker local ver_str, count = S.sysctl('kernel.version'):match('%d+.%d+.%d+'), 2 226*387f9dfdSAndroid Build Coastguard Worker if not ver_str then -- kernel.version is freeform, fallback to kernel.osrelease 227*387f9dfdSAndroid Build Coastguard Worker ver_str = S.sysctl('kernel.osrelease'):match('%d+.%d+.%d+') 228*387f9dfdSAndroid Build Coastguard Worker end 229*387f9dfdSAndroid Build Coastguard Worker local version = 0 230*387f9dfdSAndroid Build Coastguard Worker for i in ver_str:gmatch('%d+') do -- Convert 'X.Y.Z' to 0xXXYYZZ 231*387f9dfdSAndroid Build Coastguard Worker version = bit.bor(version, bit.lshift(tonumber(i), 8*count)) 232*387f9dfdSAndroid Build Coastguard Worker count = count - 1 233*387f9dfdSAndroid Build Coastguard Worker end 234*387f9dfdSAndroid Build Coastguard Worker return version 235*387f9dfdSAndroid Build Coastguard Workerend 236*387f9dfdSAndroid Build Coastguard Worker 237*387f9dfdSAndroid Build Coastguard Workerfunction M.event_reader(reader, event_type) 238*387f9dfdSAndroid Build Coastguard Worker -- Caller can specify event message binary format 239*387f9dfdSAndroid Build Coastguard Worker if event_type then 240*387f9dfdSAndroid Build Coastguard Worker assert(type(event_type) == 'string' and ffi.typeof(event_type), 'not a valid type for event reader') 241*387f9dfdSAndroid Build Coastguard Worker event_type = ffi.typeof(event_type .. '*') -- Convert type to pointer-to-type 242*387f9dfdSAndroid Build Coastguard Worker end 243*387f9dfdSAndroid Build Coastguard Worker -- Wrap reader in interface that can interpret read event messages 244*387f9dfdSAndroid Build Coastguard Worker return setmetatable({reader=reader,type=event_type}, {__index = { 245*387f9dfdSAndroid Build Coastguard Worker block = function(_ --[[self]]) 246*387f9dfdSAndroid Build Coastguard Worker return S.select { readfds = {reader.fd} } 247*387f9dfdSAndroid Build Coastguard Worker end, 248*387f9dfdSAndroid Build Coastguard Worker next = function(_ --[[self]], k) 249*387f9dfdSAndroid Build Coastguard Worker local len, ev = reader:next(k) 250*387f9dfdSAndroid Build Coastguard Worker -- Filter out only sample frames 251*387f9dfdSAndroid Build Coastguard Worker while ev and ev.type ~= S.c.PERF_RECORD.SAMPLE do 252*387f9dfdSAndroid Build Coastguard Worker len, ev = reader:next(len) 253*387f9dfdSAndroid Build Coastguard Worker end 254*387f9dfdSAndroid Build Coastguard Worker if ev and event_type then 255*387f9dfdSAndroid Build Coastguard Worker -- The perf event reader returns framed data with header and variable length 256*387f9dfdSAndroid Build Coastguard Worker -- This is going skip the frame header and cast data to given type 257*387f9dfdSAndroid Build Coastguard Worker ev = ffi.cast(event_type, ffi.cast('char *', ev) + ffi.sizeof('struct perf_event_header') + ffi.sizeof('uint32_t')) 258*387f9dfdSAndroid Build Coastguard Worker end 259*387f9dfdSAndroid Build Coastguard Worker return len, ev 260*387f9dfdSAndroid Build Coastguard Worker end, 261*387f9dfdSAndroid Build Coastguard Worker read = function(self) 262*387f9dfdSAndroid Build Coastguard Worker return self.next, self, nil 263*387f9dfdSAndroid Build Coastguard Worker end, 264*387f9dfdSAndroid Build Coastguard Worker }}) 265*387f9dfdSAndroid Build Coastguard Workerend 266*387f9dfdSAndroid Build Coastguard Worker 267*387f9dfdSAndroid Build Coastguard Workerfunction M.tracepoint_type(tp) 268*387f9dfdSAndroid Build Coastguard Worker -- Read tracepoint format string 269*387f9dfdSAndroid Build Coastguard Worker local fp = assert(io.open('/sys/kernel/debug/tracing/events/'..tp..'/format', 'r')) 270*387f9dfdSAndroid Build Coastguard Worker local fmt = fp:read '*a' 271*387f9dfdSAndroid Build Coastguard Worker fp:close() 272*387f9dfdSAndroid Build Coastguard Worker -- Parse struct fields 273*387f9dfdSAndroid Build Coastguard Worker local fields = {} 274*387f9dfdSAndroid Build Coastguard Worker for f in fmt:gmatch 'field:([^;]+;)' do 275*387f9dfdSAndroid Build Coastguard Worker table.insert(fields, f) 276*387f9dfdSAndroid Build Coastguard Worker end 277*387f9dfdSAndroid Build Coastguard Worker return string.format('struct { %s }', table.concat(fields)) 278*387f9dfdSAndroid Build Coastguard Workerend 279*387f9dfdSAndroid Build Coastguard Worker 280*387f9dfdSAndroid Build Coastguard Workerreturn M 281