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  }