├── .gitignore ├── LICENSE ├── cmd └── flen │ └── main.go ├── flen_test.go ├── README.md └── flen.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | todo 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2016 Karan Chaudhary 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmd/flen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/lafolle/flen" 7 | "os" 8 | ) 9 | 10 | var ( 11 | pkg string 12 | bucketSize int 13 | inclTests bool 14 | lenLowerLimit, lenUpperLimit int 15 | max int 16 | ) 17 | 18 | // init sets clas and flag package. 19 | func init() { 20 | flag.BoolVar(&inclTests, "t", false, "include tests files") 21 | flag.IntVar(&bucketSize, "bs", 5, "bucket size (natural number)") 22 | flag.IntVar(&lenLowerLimit, "l", 0, "min length (inclusive)") 23 | flag.IntVar(&lenUpperLimit, "u", flen.Sentinel, "max length (exclusive)") 24 | flag.IntVar(&max, "m", 0, "error if any function longer than m") 25 | flag.Usage = func() { 26 | fmt.Fprint(os.Stderr, "Usage: flen [options] \n") 27 | flag.PrintDefaults() 28 | } 29 | } 30 | 31 | func rangeAsked(ll, ul int) bool { return ll != 0 || ul != flen.Sentinel } 32 | 33 | func main() { 34 | 35 | flag.Parse() 36 | 37 | if len(flag.Args()) == 0 { 38 | flag.Usage() 39 | return 40 | } 41 | 42 | pkg = flag.Args()[0] 43 | 44 | if pkg == "" { 45 | flag.Usage() 46 | return 47 | } 48 | 49 | flenOptions := &flen.Options{ 50 | IncludeTests: inclTests, 51 | BucketSize: bucketSize, 52 | } 53 | flens, pkgpath, err := flen.GenerateFuncLens(pkg, flenOptions) 54 | if err != nil { 55 | fmt.Println(err) 56 | return 57 | } 58 | fmt.Printf("Full pkg path: %s\n\n", pkgpath) 59 | 60 | if max > 0 { 61 | for _, y := range flens { 62 | if y.Size > max { 63 | fmt.Printf("Function %s exceeds limit %d: %d", y.Name, max, y.Size) 64 | os.Exit(-1) 65 | } 66 | } 67 | } 68 | if !rangeAsked(lenLowerLimit, lenUpperLimit) { 69 | zeroLenFuncs := flens.GetZeroLenFuncs() 70 | if len(zeroLenFuncs) > 0 { 71 | fmt.Println("0 len funcs") 72 | zeroLenFuncs.Print() 73 | fmt.Println() 74 | } 75 | 76 | extImplFuncs := flens.GetExternallyImplementedFuncs() 77 | if len(extImplFuncs) > 0 { 78 | fmt.Println("Externally implemented funcs") 79 | extImplFuncs.Print() 80 | fmt.Println() 81 | } 82 | } else { 83 | rangeFlens := flens.Query(lenLowerLimit, lenUpperLimit) 84 | if len(rangeFlens) > 0 { 85 | fmt.Printf("Functions with length in range [%d, %d)\n", lenLowerLimit, lenUpperLimit) 86 | rangeFlens.Print() 87 | fmt.Println() 88 | } 89 | } 90 | fmt.Println("Histogram") 91 | if err := flens.DisplayHistogram(); err != nil { 92 | fmt.Println("failed to display histogram: ", err) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /flen_test.go: -------------------------------------------------------------------------------- 1 | package flen 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | sampleFuncLens *FuncLens 11 | sampleCode = ` 12 | func single() { 13 | println("hello single") 14 | } 15 | 16 | func double() { 17 | println("hello double") 18 | println("hello double") 19 | } 20 | 21 | func trouble() { 22 | println("hello trouble") 23 | println("hello trouble") 24 | println("hello trouble") 25 | } 26 | ` 27 | ) 28 | 29 | func TestFuncLensPrint(t *testing.T) { 30 | tests := []struct { 31 | // Test description. 32 | name string 33 | // Receiver. 34 | flens *FuncLens 35 | }{ 36 | // TODO: Add test cases. 37 | } 38 | for _, tt := range tests { 39 | tt.flens.Print() 40 | } 41 | } 42 | 43 | func TestFuncLensLen(t *testing.T) { 44 | tests := []struct { 45 | // Test description. 46 | name string 47 | // Receiver. 48 | flens *FuncLens 49 | // Expected results. 50 | want int 51 | }{ 52 | // TODO: Add test cases. 53 | } 54 | for _, tt := range tests { 55 | if got := tt.flens.Len(); got != tt.want { 56 | t.Errorf("%q. FuncLens.Len() = %v, want %v", tt.name, got, tt.want) 57 | } 58 | } 59 | } 60 | 61 | func TestFuncLensLess(t *testing.T) { 62 | tests := []struct { 63 | // Test description. 64 | name string 65 | // Receiver. 66 | flens *FuncLens 67 | // Parameters. 68 | i int 69 | j int 70 | // Expected results. 71 | want bool 72 | }{ 73 | // TODO: Add test cases. 74 | } 75 | for _, tt := range tests { 76 | if got := tt.flens.Less(tt.i, tt.j); got != tt.want { 77 | t.Errorf("%q. FuncLens.Less() = %v, want %v", tt.name, got, tt.want) 78 | } 79 | } 80 | } 81 | 82 | func TestFuncLensSwap(t *testing.T) { 83 | tests := []struct { 84 | // Test description. 85 | name string 86 | // Receiver. 87 | flens *FuncLens 88 | // Parameters. 89 | i int 90 | j int 91 | }{ 92 | // TODO: Add test cases. 93 | } 94 | for _, tt := range tests { 95 | tt.flens.Swap(tt.i, tt.j) 96 | } 97 | } 98 | 99 | func TestFuncLensComputeHistogram(t *testing.T) { 100 | tests := []struct { 101 | // Test description. 102 | name string 103 | // Receiver. 104 | flens *FuncLens 105 | // Expected results. 106 | want []int 107 | }{} 108 | for _, tt := range tests { 109 | if got := tt.flens.computeHistogram(); !reflect.DeepEqual(got, tt.want) { 110 | t.Errorf("%q. FuncLens.computeHistogram() = %v, want %v", tt.name, got, tt.want) 111 | } 112 | } 113 | } 114 | 115 | func TestFuncLensQuery(t *testing.T) { 116 | tests := []struct { 117 | // Test description. 118 | name string 119 | // Receiver. 120 | flens *FuncLens 121 | // Parameters. 122 | lowerLimit int 123 | upperLimit int 124 | // Expected results. 125 | want FuncLens 126 | }{ 127 | // TODO: Add test cases. 128 | } 129 | for _, tt := range tests { 130 | if got := tt.flens.Query(tt.lowerLimit, tt.upperLimit); !reflect.DeepEqual(got, tt.want) { 131 | t.Errorf("%q. FuncLens.Query() = %v, want %v", tt.name, got, tt.want) 132 | } 133 | } 134 | } 135 | 136 | func TestFuncLensGetZeroLenFuncs(t *testing.T) { 137 | tests := []struct { 138 | // Test description. 139 | name string 140 | // Receiver. 141 | flens *FuncLens 142 | // Expected results. 143 | want FuncLens 144 | }{ 145 | // TODO: Add test cases. 146 | } 147 | for _, tt := range tests { 148 | if got := tt.flens.GetZeroLenFuncs(); !reflect.DeepEqual(got, tt.want) { 149 | t.Errorf("%q. FuncLens.GetZeroLenFuncs() = %v, want %v", tt.name, got, tt.want) 150 | } 151 | } 152 | } 153 | 154 | func TestFuncLensGetExternallyImplementedFuncs(t *testing.T) { 155 | tests := []struct { 156 | // Test description. 157 | name string 158 | // Receiver. 159 | flens *FuncLens 160 | // Expected results. 161 | want FuncLens 162 | }{ 163 | // TODO: Add test cases. 164 | } 165 | for _, tt := range tests { 166 | if got := tt.flens.GetExternallyImplementedFuncs(); !reflect.DeepEqual(got, tt.want) { 167 | t.Errorf("%q. FuncLens.GetExternallyImplementedFuncs() = %v, want %v", tt.name, got, tt.want) 168 | } 169 | } 170 | } 171 | 172 | func TestFuncLensDisplayHistogram(t *testing.T) { 173 | tests := []struct { 174 | // Test description. 175 | name string 176 | // Receiver. 177 | flens *FuncLens 178 | }{ 179 | // TODO: Add test cases. 180 | } 181 | for _, tt := range tests { 182 | tt.flens.DisplayHistogram() 183 | } 184 | } 185 | 186 | func TestFuncLensComputePercentiles(t *testing.T) { 187 | tests := []struct { 188 | // Test description. 189 | name string 190 | // Receiver. 191 | flens *FuncLens 192 | }{ 193 | // TODO: Add test cases. 194 | } 195 | for _, tt := range tests { 196 | tt.flens.ComputePercentiles() 197 | } 198 | } 199 | 200 | func TestGenerateFuncLens(t *testing.T) { 201 | tests := []struct { 202 | // Test description. 203 | name string 204 | // Parameters. 205 | pkg string 206 | options *Options 207 | // Expected results. 208 | want FuncLens 209 | wantErr bool 210 | }{ 211 | // TODO: Add test cases. 212 | } 213 | for _, tt := range tests { 214 | got, _, err := GenerateFuncLens(tt.pkg, tt.options) 215 | if (err != nil) != tt.wantErr { 216 | t.Errorf("%q. GenerateFuncLens() error = %v, wantErr %v", tt.name, err, tt.wantErr) 217 | continue 218 | } 219 | if !reflect.DeepEqual(got, tt.want) { 220 | t.Errorf("%q. GenerateFuncLens() = %v, want %v", tt.name, got, tt.want) 221 | } 222 | } 223 | } 224 | 225 | func TestGetPkgPath(t *testing.T) { 226 | tests := []struct { 227 | // Test description. 228 | name string 229 | // Parameters. 230 | pkgname string 231 | // Expected results. 232 | want string 233 | want1 *os.PathError 234 | }{ 235 | // TODO: Add test cases. 236 | } 237 | for _, tt := range tests { 238 | got, got1 := getPkgPath(tt.pkgname) 239 | if got != tt.want { 240 | t.Errorf("%q. getPkgPath() got = %v, want %v", tt.name, got, tt.want) 241 | } 242 | if !reflect.DeepEqual(got1, tt.want1) { 243 | t.Errorf("%q. getPkgPath() got1 = %v, want %v", tt.name, got1, tt.want1) 244 | } 245 | } 246 | } 247 | 248 | /* 249 | func TestMain(m *testing.M) { 250 | os.Exit() 251 | } 252 | */ 253 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Get info on length of functions in a Go package. 2 | 3 | Given package is searched in directories provided by envs in following order: GOPATH, GOROOT. AST is generated only for Go source files present in package path, ie, `flen crypto` shall only parse `crypto.go`. For parsing `crypto/sha1`, full package path needs to be provided, ie `flen crypto/sha1`. For externally implemented functions, line number will be 0, as their is no enough information available to get their line numbers. 4 | 5 | ### Install 6 | `go get github.com/lafolle/flen/cmd/flen` 7 | 8 | ### Usage 9 | ``` 10 | Usage: flen [options] 11 | -bs int 12 | bucket size (natural number) (default 5) 13 | -l int 14 | min length (inclusive) 15 | -t include tests files 16 | -u int 17 | max length (exclusive) (default 1000000) 18 | ``` 19 | ### Examples 20 | Simple usage 21 | ``` 22 | $ flen strings 23 | Full path of pkg: /usr/local/go/src/strings 24 | Externally implemented funcs 25 | +-------+-----------+-------------------------------------------+---------+------+ 26 | | INDEX | NAME | FILEPATH | LINE NO | SIZE | 27 | +-------+-----------+-------------------------------------------+---------+------+ 28 | | 0 | IndexByte | /usr/local/go/src/strings/strings_decl.go | 0 | 0 | 29 | +-------+-----------+-------------------------------------------+---------+------+ 30 | 31 | [1-6) - ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 32 | [6-11) - ∎∎∎∎∎∎∎∎∎∎ 33 | [11-16) - ∎∎∎∎∎∎∎∎∎∎∎∎ 34 | [16-21) - ∎∎∎∎∎ 35 | [21-26) - ∎∎∎ 36 | [26-31) - ∎∎∎∎ 37 | [31-36) - ∎∎∎∎ 38 | [36-41) - ∎∎∎ 39 | [41-46) - ∎∎ 40 | [46-51) - ∎ 41 | [51-56) - 42 | [56-61) - ∎ 43 | [61-66) - 44 | $ 45 | ``` 46 | 47 | ### Range of lengths 48 | To get all function/methods whose lengths fall in given range: 49 | ``` 50 | flen -l 16 -u 41 strings 51 | Full path of pkg: /usr/local/go/src/strings 52 | Functions with length in range [16, 41) 53 | +-------+---------------------+--------------------------------------+---------+------+ 54 | | INDEX | NAME | FILEPATH | LINE NO | SIZE | 55 | +-------+---------------------+--------------------------------------+---------+------+ 56 | | 0 | Replace | /usr/local/go/src/strings/replace.go | 365 | 17 | 57 | | 1 | Seek | /usr/local/go/src/strings/reader.go | 109 | 17 | 58 | | 2 | Join | /usr/local/go/src/strings/strings.go | 388 | 18 | 59 | | 3 | WriteString | /usr/local/go/src/strings/replace.go | 432 | 20 | 60 | | 4 | isSeparator | /usr/local/go/src/strings/strings.go | 502 | 20 | 61 | | 5 | WriteString | /usr/local/go/src/strings/replace.go | 385 | 22 | 62 | | 6 | genSplit | /usr/local/go/src/strings/strings.go | 281 | 23 | 63 | | 7 | explode | /usr/local/go/src/strings/strings.go | 17 | 25 | 64 | | 8 | Replace | /usr/local/go/src/strings/replace.go | 461 | 26 | 65 | | 9 | WriteString | /usr/local/go/src/strings/replace.go | 490 | 27 | 66 | | 10 | makeGenericReplacer | /usr/local/go/src/strings/replace.go | 240 | 29 | 67 | | 11 | FieldsFunc | /usr/local/go/src/strings/strings.go | 353 | 30 | 68 | | 12 | Replace | /usr/local/go/src/strings/strings.go | 681 | 31 | 69 | | 13 | Index | /usr/local/go/src/strings/strings.go | 147 | 33 | 70 | | 14 | LastIndex | /usr/local/go/src/strings/strings.go | 184 | 33 | 71 | | 15 | lookup | /usr/local/go/src/strings/replace.go | 193 | 33 | 72 | | 16 | Map | /usr/local/go/src/strings/strings.go | 422 | 37 | 73 | | 17 | WriteString | /usr/local/go/src/strings/replace.go | 312 | 38 | 74 | | 18 | makeStringFinder | /usr/local/go/src/strings/search.go | 48 | 40 | 75 | +-------+---------------------+--------------------------------------+---------+------+ 76 | 77 | [1-6) - ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 78 | [6-11) - ∎∎∎∎∎∎∎∎∎∎ 79 | [11-16) - ∎∎∎∎∎∎∎∎∎∎∎∎ 80 | [16-21) - ∎∎∎∎∎ 81 | [21-26) - ∎∎∎ 82 | [26-31) - ∎∎∎∎ 83 | [31-36) - ∎∎∎∎ 84 | [36-41) - ∎∎∎ 85 | [41-46) - ∎∎ 86 | [46-51) - ∎ 87 | [51-56) - 88 | [56-61) - ∎ 89 | [61-66) - 90 | ``` 91 | 92 | ### Including test files 93 | By default, test files are ignored. Enable parsing test files by `-t` flag 94 | ``` 95 | $ flen -t strings 96 | Full path of pkg: /usr/local/go/src/strings 97 | 98 | Externally implemented funcs 99 | +-------+-----------+-------------------------------------------+---------+------+ 100 | | INDEX | NAME | FILEPATH | LINE NO | SIZE | 101 | +-------+-----------+-------------------------------------------+---------+------+ 102 | | 0 | IndexByte | /usr/local/go/src/strings/strings_decl.go | 0 | 0 | 103 | +-------+-----------+-------------------------------------------+---------+------+ 104 | 105 | [1-6) - ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 106 | [6-11) - ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 107 | [11-16) - ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 108 | [16-21) - ∎∎∎∎∎∎∎∎∎∎∎∎ 109 | [21-26) - ∎∎∎∎∎ 110 | [26-31) - ∎∎∎∎∎∎ 111 | [31-36) - ∎∎∎∎ 112 | [36-41) - ∎∎∎∎ 113 | [41-46) - ∎∎∎∎∎∎∎ 114 | [46-51) - ∎ 115 | [51-56) - ∎ 116 | [56-61) - ∎ 117 | [61-66) - 118 | [66-71) - ∎ 119 | [71-76) - 120 | [76-81) - 121 | [81-86) - 122 | [86-91) - 123 | [91-96) - 124 | [96-101) - 125 | [101-106) - 126 | [106-111) - 127 | [111-116) - 128 | [116-121) - 129 | [121-126) - 130 | [126-131) - 131 | [131-136) - 132 | [136-141) - 133 | [141-146) - 134 | [146-151) - 135 | [151-156) - 136 | [156-161) - 137 | [161-166) - 138 | [166-171) - 139 | [171-176) - 140 | [176-181) - 141 | [181-186) - 142 | [186-191) - 143 | [191-196) - 144 | [196-201) - 145 | [201-206) - 146 | [206-211) - 147 | [211-216) - 148 | [216-221) - 149 | [221-226) - 150 | [226-231) - 151 | [231-236) - 152 | [236-241) - 153 | [241-246) - 154 | [246-251) - 155 | [251-256) - 156 | [256-261) - 157 | [261-266) - ∎ 158 | [266-271) - 159 | ``` 160 | -------------------------------------------------------------------------------- /flen.go: -------------------------------------------------------------------------------- 1 | package flen 2 | 3 | import ( 4 | "fmt" 5 | "github.com/olekukonko/tablewriter" 6 | "go/ast" 7 | "go/parser" 8 | "go/token" 9 | "os" 10 | "runtime" 11 | "sort" 12 | "strconv" 13 | "strings" 14 | "text/tabwriter" 15 | ) 16 | 17 | type funcLen struct { 18 | Name string 19 | Size int // len of function 20 | Filepath string 21 | Lbrace, Rbrace int 22 | Type funcType 23 | } 24 | 25 | // Options provides the user of this package to configre it 26 | // for according to his/her needs. 27 | type Options struct { 28 | IncludeTests bool 29 | BucketSize int 30 | } 31 | 32 | type funcType int 33 | 34 | const ( 35 | // Sentinel defines max length of func this pkg can handle 36 | Sentinel = 1000000 37 | defaultBucketSize = 5 38 | defaultIncludeTests = false 39 | streakChar = "∎" 40 | 41 | implemented funcType = iota 42 | implementedAtRuntime 43 | ) 44 | 45 | var ( 46 | goroot = os.Getenv("GOROOT") 47 | pkgpath string 48 | gopath = os.Getenv("GOPATH") 49 | opts *Options 50 | ) 51 | 52 | // Determining GOROOT for a platform needs to be done explicitly, 53 | // as (it seems) std library does not provide any. First preference is given 54 | // to GOROOT environment variable, then we go for static definations. 55 | func init() { 56 | if goroot == "" { 57 | return 58 | } 59 | platform := runtime.GOOS 60 | switch platform { 61 | case "linux", "darwin", "freebsd": 62 | goroot = "/usr/local/go" 63 | case "windows": 64 | goroot = "c:\\Go" 65 | default: 66 | panic(fmt.Sprintf("platform not supported: %s", platform)) 67 | } 68 | } 69 | 70 | // FuncLens is the main object which flen pkg exposes to client. 71 | // All the operation are done on this. 72 | type FuncLens []funcLen 73 | 74 | func (flens *FuncLens) Print() { 75 | data := make([][]string, 0) 76 | for i, f := range *flens { 77 | data = append(data, []string{strconv.Itoa(i), f.Name, f.Filepath, strconv.Itoa(f.Lbrace), strconv.Itoa(f.Size)}) 78 | } 79 | table := tablewriter.NewWriter(os.Stdout) 80 | table.SetHeader([]string{"index", "name", "filepath", "line no", "size"}) 81 | table.AppendBulk(data) 82 | table.Render() 83 | } 84 | 85 | // Sort interface is implemented for FuncLens type. 86 | // Sorting is needed for computing percentiles. 87 | func (flens *FuncLens) Len() int { return len(*flens) } 88 | func (flens *FuncLens) Less(i, j int) bool { 89 | switch { 90 | case (*flens)[i].Size < (*flens)[j].Size: 91 | return true 92 | case (*flens)[i].Size > (*flens)[j].Size: 93 | return false 94 | case strings.Compare((*flens)[i].Name, (*flens)[j].Name) == -1: 95 | return true 96 | case strings.Compare((*flens)[i].Name, (*flens)[j].Name) == 1: 97 | return false 98 | } 99 | return false 100 | } 101 | func (flens *FuncLens) Swap(i, j int) { (*flens)[i], (*flens)[j] = (*flens)[j], (*flens)[i] } 102 | 103 | // createHistogram computes and returns slice of histogram data points. 104 | func (flens *FuncLens) computeHistogram() []int { 105 | var hg []int 106 | var x int 107 | if len(*flens) == 0 { 108 | return nil 109 | } 110 | // find max func len 111 | var maxFlen int = -Sentinel 112 | for _, flen := range *flens { 113 | if flen.Size > maxFlen { 114 | maxFlen = flen.Size 115 | } 116 | } 117 | hglen := maxFlen / opts.BucketSize 118 | hg = make([]int, hglen+1) 119 | for _, v := range *flens { 120 | if v.Size > 0 { 121 | x = v.Size % opts.BucketSize 122 | if x == 0 { 123 | x = v.Size/opts.BucketSize - 1 124 | } else { 125 | x = v.Size / opts.BucketSize 126 | } 127 | hg[x]++ 128 | } 129 | } 130 | return hg 131 | } 132 | 133 | // It will be helpful to see all funcs with length falling in a range. 134 | // lowerLimit is inclusive, upperLimit is exclusive. 135 | func (flens *FuncLens) Query(lowerLimit, upperLimit int) FuncLens { 136 | result := make(FuncLens, 0) 137 | for _, f := range *flens { 138 | if f.Size >= lowerLimit && f.Size < upperLimit { 139 | result = append(result, f) 140 | } 141 | } 142 | //sort.Sort(&result) 143 | return result 144 | } 145 | 146 | func (flens *FuncLens) GetZeroLenFuncs() FuncLens { 147 | result := make(FuncLens, 0) 148 | for _, f := range *flens { 149 | if f.Size == 0 && f.Type == implemented { 150 | result = append(result, f) 151 | } 152 | } 153 | return result 154 | } 155 | 156 | func (flens *FuncLens) GetExternallyImplementedFuncs() FuncLens { 157 | result := make(FuncLens, 0) 158 | for _, f := range *flens { 159 | if f.Type == implementedAtRuntime { 160 | result = append(result, f) 161 | } 162 | } 163 | return result 164 | } 165 | 166 | // Display histogram data points 167 | func (flens *FuncLens) DisplayHistogram() error { 168 | var ( 169 | start int = 1 170 | hg []int = flens.computeHistogram() 171 | hglen int = len(hg) 172 | streak string 173 | ) 174 | tabw := new(tabwriter.Writer) 175 | tabw.Init(os.Stdout, 0, 4, 0, '\t', 0) 176 | fmt.Fprint(tabw, "") 177 | for i := 0; i < hglen; i++ { 178 | bucketrange := fmt.Sprintf("[%d-%d)", start, start+opts.BucketSize) 179 | streak = "" 180 | for j := 0; j < hg[i]; j++ { 181 | streak = fmt.Sprintf("%s%s", streak, streakChar) 182 | } 183 | fmt.Fprintln(tabw, fmt.Sprintf("%s\t-\t%s", bucketrange, streak)) 184 | start += opts.BucketSize 185 | } 186 | return tabw.Flush() 187 | } 188 | 189 | // Stats generate statstics on length of functions in a package. 190 | func (flens *FuncLens) ComputePercentiles() { 191 | var n = len(*flens) 192 | sort.Sort(flens) 193 | prcntls := make([]float32, n) 194 | for i := 0; i < n; i++ { 195 | prcntls[i] = float32((100*i + 50) / n) 196 | } 197 | /* 198 | for i, f := range *flens { 199 | fmt.Println(f.Size, prcntls[i]) 200 | } 201 | */ 202 | } 203 | 204 | // GenerateFuncLens generates FuncLens for the given package. If options.InclTests is true, 205 | // functions in tests are also evaluated. For ease in readibility of func lens in table, 206 | // result is sorted. 207 | func GenerateFuncLens(pkg string, options *Options) (FuncLens, string, error) { 208 | opts = options 209 | if opts == nil { 210 | opts = &Options{ 211 | IncludeTests: defaultIncludeTests, 212 | BucketSize: defaultBucketSize, 213 | } 214 | } 215 | pkgpath, err := getPkgPath(pkg) 216 | if err != nil { 217 | return nil, pkgpath, err 218 | } 219 | 220 | fset := token.NewFileSet() 221 | pkgs, ferr := parser.ParseDir(fset, pkgpath, func(f os.FileInfo) bool { 222 | if opts.IncludeTests { 223 | return true 224 | } 225 | return !strings.HasSuffix(f.Name(), "_test.go") 226 | }, parser.AllErrors) 227 | if ferr != nil { 228 | panic(ferr) 229 | } 230 | flens := make(FuncLens, 0) 231 | for _, v := range pkgs { 232 | for filepath, astf := range v.Files { 233 | for _, decl := range astf.Decls { 234 | ast.Inspect(decl, func(node ast.Node) bool { 235 | var ( 236 | funcname string 237 | diff int 238 | lb, rb token.Pos 239 | rln, lln int 240 | ftype funcType 241 | ) 242 | 243 | if x, ok := node.(*ast.FuncDecl); ok { 244 | ftype = implemented 245 | funcname = x.Name.Name 246 | if x.Body == nil { 247 | ftype = implementedAtRuntime // externally implemented 248 | } else { 249 | lb = x.Body.Lbrace 250 | rb = x.Body.Rbrace 251 | if !lb.IsValid() || !rb.IsValid() { 252 | return false 253 | } 254 | rln = fset.Position(rb).Line 255 | lln = fset.Position(lb).Line 256 | diff = rln - lln - 1 257 | if diff == -1 { 258 | diff = 1 // single line func 259 | } 260 | } 261 | flens = append(flens, funcLen{ 262 | Name: funcname, 263 | Size: diff, 264 | Filepath: filepath, 265 | Lbrace: lln, 266 | Rbrace: rln, 267 | Type: ftype, 268 | }) 269 | } 270 | return false 271 | 272 | }) 273 | } 274 | } 275 | } 276 | sort.Sort(&flens) 277 | return flens, pkgpath, nil 278 | } 279 | 280 | // getPkgPath tries to get path of pkg. Path is platform dependent. 281 | // First pkg is checked in GOPATH, then in GOROOT, then err. 282 | func getPkgPath(pkgname string) (string, *os.PathError) { 283 | var ppath string 284 | if gopath != "" { 285 | for _, godir := range strings.Split(gopath, string(os.PathListSeparator)) { 286 | ppath = strings.Join([]string{godir, "src", pkgname}, string(os.PathSeparator)) 287 | _, err := os.Stat(ppath) 288 | if err != nil { 289 | continue 290 | } 291 | return ppath, nil 292 | } 293 | } 294 | ppath = strings.Join([]string{goroot, "src", pkgname}, string(os.PathSeparator)) 295 | _, err := os.Stat(ppath) 296 | if err != nil { 297 | return "", err.(*os.PathError) 298 | } 299 | return ppath, nil 300 | } 301 | --------------------------------------------------------------------------------