github.com/luchsh/agentlib.go@v0.0.0-20221115155834-ffd0caec4d72/jgo/mkjvmti.go (about) 1 // Copyright 2020 chuanshenglu@gmail.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build ignore 16 17 package main 18 19 import ( 20 "bufio" 21 "fmt" 22 "io" 23 "os" 24 "strings" 25 "time" 26 ) 27 28 var ( 29 javaHome string 30 ) 31 32 func init() { 33 if javaHome = os.Getenv("JAVA_HOME"); javaHome == "" { 34 panic("JAVA_HOME env not found!") 35 } 36 } 37 38 type jvmtiPipe struct { 39 out io.Writer 40 in io.Reader 41 consts [][]string // constatn definition, converted from enum 42 fns []fn 43 jvmtiErrors []string 44 } 45 46 // parsed function info 47 type fn struct { 48 name string 49 ret string 50 ptype []string // params types 51 pname []string // params arg names 52 } 53 54 // e.g. 55 // jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env, 56 // jvmtiEventMode mode, 57 // jvmtiEvent event_type, 58 // jthread event_thread, 59 // ...); 60 func parseJvmtiFuncDecl(s string) fn { 61 var f fn 62 s = strings.TrimSpace(s) 63 s = strings.Replace(s, "(JNICALL *", "", 1) 64 s = strings.Replace(s, ") (", " (", 1) 65 ilb := strings.Index(s, "(") 66 irb := strings.Index(s, ")") 67 l := s[0:ilb] 68 params := s[ilb+1 : irb] 69 fds := strings.Fields(l) 70 f.ret = fds[0] 71 f.name = fds[1] 72 73 for _, p := range strings.Split(params, ",") { 74 fds := strings.Fields(p) 75 if len(fds) == 1 { 76 // SKIP ... 77 //f.ptype = append(f.ptype, fds[0]) 78 //f.pname = append(f.pname, "") 79 } else if len(fds) >= 2 { 80 f.ptype = append(f.ptype, strings.Join(fds[0:len(fds)-1], " ")) 81 f.pname = append(f.pname, fds[len(fds)-1]) 82 } 83 } 84 return f 85 } 86 87 func (f *fn) cdecl() string { 88 var sb strings.Builder 89 sb.WriteString(fmt.Sprintf("%s jvmti%s(", f.ret, f.name)) 90 for i := 0; i < len(f.ptype); i++ { 91 sb.WriteString(fmt.Sprintf("%s %s", f.ptype[i], f.pname[i])) 92 if i < len(f.ptype)-1 { 93 sb.WriteString(", ") 94 } 95 } 96 sb.WriteString(")") 97 return sb.String() 98 } 99 100 // converted cgo 101 func (f *fn) cgoimpl(lnpfx string) string { 102 var sb strings.Builder 103 sb.WriteString(fmt.Sprintf("%sstatic ", lnpfx)) 104 sb.WriteString(f.cdecl()) 105 sb.WriteString(" {\n") 106 sb.WriteString(fmt.Sprintf("%s return (*env)->%s(", lnpfx, f.name)) 107 for i := 0; i < len(f.ptype); i++ { 108 if len(f.pname[i]) > 0 { 109 sb.WriteString(f.pname[i]) 110 } else { 111 sb.WriteString(f.ptype[i]) 112 } 113 if i < len(f.ptype)-1 { 114 sb.WriteString(", ") 115 } 116 } 117 sb.WriteString(fmt.Sprintf(");\n%s}", lnpfx)) 118 return sb.String() 119 } 120 121 // convert C type decl to Go 122 func goTypeOfC(ct string) string { 123 ct = strings.Replace(ct, "const ", "", 1) 124 famousTypes := map[string]string{ 125 "char*": "*C.char", 126 "char**": "**C.char", 127 "unsigned char**": "**C.uchar", 128 "unsigned char*": "*C.uchar", 129 "void*": "unsafe.Pointer", 130 "void**": "*unsafe.Pointer", 131 "...": "[]interface{}", 132 } 133 if v, ok := famousTypes[ct]; ok { 134 return v 135 } 136 fds := strings.Fields(ct) 137 if len(fds) == 1 { 138 ct = fmt.Sprintf("C.%s", fds[0]) 139 } 140 for strings.HasSuffix(ct, "*") { 141 ct = ct[0 : len(ct)-1] 142 ct = "*" + ct 143 } 144 return ct 145 } 146 147 func toGoPrivName(name string) string { 148 if name == "" { 149 return name 150 } 151 return strings.ToLower(name[0:1]) + name[1:] 152 } 153 154 func (f *fn) goimpl() string { 155 var sb strings.Builder 156 //sb.WriteString("//TODO: manual adjustment needed here\n") 157 sb.WriteString(fmt.Sprintf("func (jvmti jvmtiEnv) %s(", toGoPrivName(f.name))) 158 for i := 1; i < len(f.ptype); i++ { 159 if len(f.pname[i]) > 0 { 160 sb.WriteString(f.pname[i]) 161 } else { 162 sb.WriteString("args") 163 } 164 sb.WriteString(fmt.Sprintf(" %s", goTypeOfC(f.ptype[i]))) 165 if i < len(f.ptype)-1 { 166 sb.WriteString(", ") 167 } 168 } 169 sb.WriteString(fmt.Sprintf(") C.%s {\n", f.ret)) 170 sb.WriteString(fmt.Sprintf(" return C.jvmti%s(", f.name)) 171 for i := 0; i < len(f.ptype); i++ { 172 if i == 0 { 173 sb.WriteString("jvmti.raw(), ") 174 continue 175 } 176 if len(f.pname[i]) > 0 { 177 sb.WriteString(f.pname[i]) 178 } else { 179 sb.WriteString("args") 180 } 181 if i < len(f.ptype)-1 { 182 sb.WriteString(", ") 183 } 184 } 185 sb.WriteString(")\n}\n") 186 return sb.String() 187 } 188 189 func (jp *jvmtiPipe) parse() { 190 rd := bufio.NewReader(jp.in) 191 // load enum consts 192 for { 193 bs, _, e := rd.ReadLine() 194 if e == io.EOF { 195 return 196 } 197 ln := string(bs) 198 if strings.Contains(ln, "enum {") { 199 var cconsts []string 200 for { 201 bs, _, e := rd.ReadLine() 202 ln := string(bs) 203 if e == io.EOF || strings.Contains(ln, "}") { 204 break 205 } 206 cconsts = append(cconsts, ln) 207 // save JVMTI_ERROR_* for generating describer 208 ln = strings.TrimSpace(ln) 209 if strings.HasPrefix(ln, "JVMTI_ERROR_") && !strings.HasPrefix(ln, "JVMTI_ERROR_MAX") { 210 jp.jvmtiErrors = append(jp.jvmtiErrors, strings.Fields(ln)[0]) 211 } 212 } 213 jp.consts = append(jp.consts, cconsts) 214 } else if strings.HasPrefix(ln, "typedef struct jvmtiInterface_1_ {") { 215 for { 216 bs, _, e := rd.ReadLine() 217 ln := string(bs) 218 if e == io.EOF || strings.Contains(ln, "}") { 219 break 220 } 221 222 if strings.Contains(ln, "(JNICALL") && strings.Contains(ln, "(jvmtiEnv* env,") { 223 var sb strings.Builder 224 sb.WriteString(ln) 225 for { 226 bs, _, e := rd.ReadLine() 227 if e != nil { 228 panic("e") 229 } 230 ln := string(bs) 231 sb.WriteString(ln) 232 if strings.Contains(ln, ")") { 233 break 234 } 235 } 236 jp.fns = append(jp.fns, parseJvmtiFuncDecl(sb.String())) 237 } 238 } 239 } 240 } 241 } 242 243 func (jp *jvmtiPipe) printCgoWrapper() { 244 for _, fn := range jp.fns { 245 fmt.Fprintf(jp.out, "%s\n", fn.cgoimpl("// ")) 246 } 247 } 248 249 func (jp *jvmtiPipe) printGoWrapper() { 250 for _, fn := range jp.fns { 251 fmt.Fprintf(jp.out, "%s\n", fn.goimpl()) 252 } 253 } 254 255 func (jp *jvmtiPipe) printJvmtiErrorDesc() { 256 fmt.Fprintf(jp.out, `func describeJvmtiError(err int) string { 257 switch (err) { 258 `) 259 260 for _, e := range jp.jvmtiErrors { 261 fmt.Fprintf(jp.out, ` 262 case %s: 263 return "%s" 264 `, e, e) 265 } 266 267 fmt.Fprintf(jp.out, `default: 268 panic(fmt.Sprintf("Unknown JVMTI error code: %%d", err)) 269 } 270 return "" 271 }`) 272 } 273 274 func (jp *jvmtiPipe) printJvmtiDef() { 275 fmt.Fprintf(jp.out, "%s\n", 276 `// jvmtiEnv corresponds to jvmtiEnv* 277 type jvmtiEnv uintptr 278 279 func (jvmti jvmtiEnv) raw() *C.jvmtiEnv { 280 return (*C.jvmtiEnv)(unsafe.Pointer(jvmti)) 281 } 282 283 func (jvmti jvmtiEnv) asPointer() unsafe.Pointer { 284 return unsafe.Pointer(jvmti) 285 }`) 286 } 287 288 func (jp *jvmtiPipe) print() { 289 jp.printHeader() 290 jp.printCgoWrapper() 291 fmt.Fprintf(jp.out, "import \"C\"\n\n") 292 fmt.Fprintf(jp.out, `import ( 293 "fmt" 294 "unsafe" 295 ) 296 `) 297 298 // consts 299 for _, cc := range jp.consts { 300 fmt.Fprintf(jp.out, "const (\n") 301 for _, c := range cc { 302 if strings.HasSuffix(c, ",") { 303 c = c[0 : len(c)-1] 304 } 305 fmt.Fprintf(jp.out, "%s\n", c) 306 } 307 fmt.Fprintf(jp.out, ")\n\n") 308 } 309 310 fmt.Fprintf(jp.out, "\n\n") 311 jp.printJvmtiDef() 312 fmt.Fprintf(jp.out, "\n\n") 313 jp.printGoWrapper() 314 jp.printJvmtiErrorDesc() 315 } 316 317 func (jp *jvmtiPipe) Pump() { 318 jp.parse() 319 jp.print() 320 } 321 322 func (jp *jvmtiPipe) printHeader() { 323 fmt.Fprintf(jp.out, "// Copyright %d chuanshenglu@gmail.com\n", time.Now().Year()) 324 fmt.Fprintf(jp.out, `// 325 // Licensed under the Apache License, Version 2.0 (the "License"); 326 // you may not use this file except in compliance with the License. 327 // You may obtain a copy of the License at 328 // 329 // http://www.apache.org/licenses/LICENSE-2.0 330 // 331 // Unless required by applicable law or agreed to in writing, software 332 // distributed under the License is distributed on an "AS IS" BASIS, 333 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 334 // See the License for the specific language governing permissions and 335 // limitations under the License.\n\n 336 337 // Generated by jgo/mkjvmti.go, please do not edit this file directly! 338 339 package jgo 340 341 // #include <jvmti.h> 342 // 343 `) 344 } 345 346 func main() { 347 jvmtiH := fmt.Sprintf("%s/include/jvmti.h", javaHome) 348 fin, err := os.Open(jvmtiH) 349 if err != nil { 350 panic(err) 351 } 352 353 jp := &jvmtiPipe{ 354 out: os.Stdout, 355 in: fin, 356 } 357 jp.Pump() 358 }