Skip to content

Commit

Permalink
perf: remove unnecessary json encode from wasm
Browse files Browse the repository at this point in the history
  • Loading branch information
Zxilly committed May 30, 2024
1 parent 1cc43e5 commit be0f2e3
Show file tree
Hide file tree
Showing 17 changed files with 189 additions and 97 deletions.
6 changes: 2 additions & 4 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,13 @@ linters-settings:
# Default: false
require-specific: true

# define the import orders
gci:
sections:
# Standard section: captures all standard packages.
- standard
# Default section: catchall that is not standard or custom
- default
# linters that related to local tool, so they should be separated
- prefix(syscall/js) # fixme: workaround, wait for https://github.com/daixiang0/gci/pull/208 merged
- localmodule
custom-order: true

staticcheck:
# SAxxxx checks in https://staticcheck.io/docs/configuration/options/#checks
Expand Down
23 changes: 6 additions & 17 deletions cmd/wasm/main_wasm.go → cmd/wasm/main_js_wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,38 @@ import (
"fmt"
"log/slog"
"syscall/js"
"unsafe"

"github.com/Zxilly/go-size-analyzer/internal"
"github.com/Zxilly/go-size-analyzer/internal/printer"
"github.com/Zxilly/go-size-analyzer/internal/printer/wasm"
"github.com/Zxilly/go-size-analyzer/internal/utils"
)

func analyze(_ js.Value, args []js.Value) any {
utils.InitLogger(slog.LevelDebug)

name := args[0].String()
data := make([]byte, args[1].Length())
length := args[1].Length()
data := make([]byte, length)
js.CopyBytesToGo(data, args[1])

reader := bytes.NewReader(data)

result, err := internal.Analyze(name, reader, uint64(len(data)), internal.Options{
result, err := internal.Analyze(name, reader, uint64(length), internal.Options{
SkipDisasm: true,
})
if err != nil {
fmt.Printf("Error: %v\n", err)
return js.ValueOf(nil)
}

buf := new(bytes.Buffer)
err = printer.JSON(result, &printer.JSONOption{
Writer: buf,
Indent: nil,
HideDetail: true,
})

if err != nil {
fmt.Printf("Error: %v\n", err)
return js.ValueOf(nil)
}

return js.ValueOf(unsafe.String(unsafe.SliceData(buf.Bytes()), buf.Len()))
return wasm.JavaScript(result)
}

func main() {
utils.ApplyMemoryLimit()

js.Global().Set("gsa_analyze", js.FuncOf(analyze))
js.Global().Get("console").Call("log", "Go size analyzer initialized")

select {}
}
5 changes: 3 additions & 2 deletions internal/disasm/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ func extractAmd64(code []byte, pc uint64) []PossibleStr {

for len(code) > 0 {
inst, err := x86asm.Decode(code, 64)
size := inst.Len
if err != nil || size == 0 || inst.Op == 0 {
size := 0
if err != nil || inst.Len == 0 || inst.Op == 0 {
size = 1
} else {
size = inst.Len
if inst.Op != x86asm.NOP {
insts = append(insts, x86PosInst{pc: pc, inst: inst})
}
Expand Down
File renamed without changes.
37 changes: 0 additions & 37 deletions internal/entity/file.go
Original file line number Diff line number Diff line change
@@ -1,49 +1,12 @@
package entity

import (
"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
)

type File struct {
FilePath string `json:"file_path"`
Functions []*Function `json:"functions"`

Pkg *Package `json:"-"`
}

var FileMarshalerCompact = json.MarshalFuncV2[File](func(encoder *jsontext.Encoder, file File, options json.Options) error {
err := encoder.WriteToken(jsontext.ObjectStart)
if err != nil {
return err
}

if err = json.MarshalEncode(encoder, "file_path", options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, file.FilePath, options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, "size", options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, file.FullSize(), options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, "pcln_size", options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, file.PclnSize(), options); err != nil {
return err
}

err = encoder.WriteToken(jsontext.ObjectEnd)
if err != nil {
return err
}
return nil
})

func (f *File) FullSize() uint64 {
size := uint64(0)
for _, fn := range f.Functions {
Expand Down
15 changes: 15 additions & 0 deletions internal/entity/file_js_wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build js && wasm

package entity

import (
"syscall/js"
)

func (f *File) MarshalJavaScript() js.Value {
return js.ValueOf(map[string]any{
"file_path": f.FilePath,
"size": f.FullSize(),
"pcln_size": f.PclnSize(),
})
}
40 changes: 40 additions & 0 deletions internal/entity/file_normal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//go:build !js && !wasm

package entity

import (
"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
)

var FileMarshalerCompact = json.MarshalFuncV2[File](func(encoder *jsontext.Encoder, file File, options json.Options) error {
err := encoder.WriteToken(jsontext.ObjectStart)
if err != nil {
return err
}

if err = json.MarshalEncode(encoder, "file_path", options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, file.FilePath, options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, "size", options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, file.FullSize(), options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, "pcln_size", options); err != nil {
return err
}
if err = json.MarshalEncode(encoder, file.PclnSize(), options); err != nil {
return err
}

err = encoder.WriteToken(jsontext.ObjectEnd)
if err != nil {
return err
}
return nil
})
34 changes: 34 additions & 0 deletions internal/entity/package_js_wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//go:build js && wasm

package entity

import (
"syscall/js"

"github.com/samber/lo"
)

func (m PackageMap) MarshalJavaScript() js.Value {
ret := map[string]any{}

for k, v := range m {
ret[k] = v.MarshalJavaScript()
}

return js.ValueOf(ret)
}

func (p *Package) MarshalJavaScript() js.Value {
var symbols, files []any
symbols = lo.Map(p.Symbols, func(s *Symbol, _ int) any { return s.MarshalJavaScript() })
files = lo.Map(p.Files, func(f *File, _ int) any { return f.MarshalJavaScript() })

return js.ValueOf(map[string]any{
"name": p.Name,
"type": p.Type,
"size": p.Size,
"symbols": symbols,
"subPackages": p.SubPackages.MarshalJavaScript(),
"files": files,
})
}
22 changes: 22 additions & 0 deletions internal/entity/section_js_wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build js && wasm

package entity

import (
"syscall/js"
)

func (s Section) MarshalJavaScript() js.Value {
return js.ValueOf(map[string]any{
"name": s.Name,
"size": s.Size,
"file_size": s.FileSize,
"known_size": s.KnownSize,
"offset": s.Offset,
"end": s.End,
"addr": s.Addr,
"addr_end": s.AddrEnd,
"only_in_memory": s.OnlyInMemory,
"debug": s.Debug,
})
}
16 changes: 16 additions & 0 deletions internal/entity/symbol_js_wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//go:build js && wasm

package entity

import (
"syscall/js"
)

func (s *Symbol) MarshalJavaScript() js.Value {
return js.ValueOf(map[string]any{
"name": s.Name,
"addr": s.Addr,
"size": s.Size,
"type": s.Type,
})
}
13 changes: 13 additions & 0 deletions internal/printer/wasm/obj_js_wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build js && wasm

package wasm

import (
"syscall/js"

"github.com/Zxilly/go-size-analyzer/internal/result"
)

func JavaScript(r *result.Result) js.Value {
return r.MarshalJavaScript()
}
25 changes: 25 additions & 0 deletions internal/result/result_js_wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build js && wasm

package result

import (
"syscall/js"

"github.com/samber/lo"

"github.com/Zxilly/go-size-analyzer/internal/entity"
)

func (r *Result) MarshalJavaScript() js.Value {
var sections []any
sections = lo.Map(r.Sections, func(s *entity.Section, _ int) any {
return s.MarshalJavaScript()
})

return js.ValueOf(map[string]any{
"name": r.Name,
"size": r.Size,
"packages": r.Packages.MarshalJavaScript(),
"sections": sections,
})
}
2 changes: 1 addition & 1 deletion scripts/wasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def require_binaryen():
"build",
"-trimpath",
"-o", tmp_file.name,
"./cmd/wasm/main_wasm.go"
"./cmd/wasm/main_js_wasm.go"
],
text=True,
cwd=get_project_root(),
Expand Down
19 changes: 11 additions & 8 deletions ui/src/explorer/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {ReactNode, useEffect, useMemo} from "react";
import {useAsync} from "react-use";
import gsa from "../../gsa.wasm?init";
import {Entry} from "../tool/entry.ts";
import {loadDataFromWasmResult} from "../tool/utils.ts";
import {Dialog, DialogContent, DialogContentText, DialogTitle} from "@mui/material";
import {FileSelector} from "./file_selector.tsx";
import TreeMap from "../TreeMap.tsx";
Expand All @@ -15,6 +14,8 @@ type ModalState = {
content: ReactNode
}

declare function gsa_analyze(name: string, data: Uint8Array): import("../generated/schema.ts").Result;

export const App: React.FC = () => {
const go = useMemo(() => new Go(), [])

Expand All @@ -34,24 +35,26 @@ export const App: React.FC = () => {

const [modalState, setModalState] = React.useState<ModalState>({isOpen: false})

const {value: jsonResult, loading: analyzing} = useAsync(async () => {
const {value: result, loading: analyzing} = useAsync(async () => {
if (!file) {
return
}

const bytes = await file.arrayBuffer()
const uint8 = new Uint8Array(bytes)

return gsa_analyze(file.name, uint8)
const r = gsa_analyze(file.name, uint8)
console.log(r)
return r
}, [file])

const entry = useMemo(() => {
if (!jsonResult) {
if (!result) {
return null
}

return new Entry(loadDataFromWasmResult(jsonResult))
}, [jsonResult])
return new Entry(result)
}, [result])

useEffect(() => {
if (loadError) {
Expand Down Expand Up @@ -84,7 +87,7 @@ export const App: React.FC = () => {
title: "Analyzing",
content: <DialogContentText>Analyzing binary...</DialogContentText>
})
} else if (!analyzing && !jsonResult && !entry) {
} else if (!analyzing && !result && !entry) {
setModalState({
isOpen: true,
title: "Error",
Expand All @@ -95,7 +98,7 @@ export const App: React.FC = () => {
} else {
setModalState({isOpen: false})
}
}, [loadError, loading, file, jsonResult, analyzing, inst, entry])
}, [loadError, loading, file, result, analyzing, inst, entry])

return <>
<Dialog
Expand Down
2 changes: 1 addition & 1 deletion ui/src/explorer/file_selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const FileSelector = ({handler}: {
<DialogContent>
<DialogContentText>
The selected binary {pendingFile?.name} has a size of {formatBytes(pendingFile?.size || 0)}.
It is not recommended to use the wasm version for binary files larger than 30MB.
It is not recommended to use the wasm version for binary files larger than 30 MB.
</DialogContentText>
</DialogContent>
<DialogActions>
Expand Down
1 change: 0 additions & 1 deletion ui/src/explorer/gsa.d.ts

This file was deleted.

Loading

0 comments on commit be0f2e3

Please sign in to comment.