├── .gitignore ├── README.md ├── binaries ├── darwin │ └── amd64 │ │ └── goperf ├── freebsd │ ├── 386 │ │ └── goperf │ └── amd64 │ │ └── goperf ├── linux │ ├── 386 │ │ └── goperf │ └── amd64 │ │ └── goperf └── windows │ ├── 386 │ └── goperf.exe │ └── amd64 │ └── goperf.exe ├── build.sh ├── goperf ├── goperf.go ├── httputils ├── httputils.go ├── httputils.test ├── httputils_test.go ├── out.prof ├── pprof002.svg └── test_data │ ├── test.html │ └── test_basic.html ├── perf └── perf.go ├── readme_imgs ├── Fetch.png ├── GoPerf.png └── GoPerfOutput.png └── request ├── combine.go ├── fetch.go ├── fetchall.go └── structs.go /.gitignore: -------------------------------------------------------------------------------- 1 | *swp 2 | *.swo 3 | output.json 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goperf 2 | A highly concurrant website load tester with a simple intuitive command line syntax. 3 | 4 | ![Alt text](readme_imgs/GoPerf.png?raw=true "GoPerf") 5 | 6 | The header image shows goperf running on a 32 cpu machine. The machine being tested was a traditional web stack with a load balancer and 10 app servers. 7 | 8 | Goperf fetches the html document as well as all the img, css, and js assets in an effort to realistically simulate a a basic browser request to your site. *Support for follow up ajax requests is aimed for the next release* 9 | 10 | Goperf also supports simple http request headers like user-agent and cookies strings. 11 | 12 | ## Prebuilt Binaries 13 | [Darwin 64 bit](https://github.com/gnulnx/goperf/raw/master/binaries/darwin/amd64/goperf) 14 | 15 | [FreeBSD 64 bit](https://github.com/gnulnx/goperf/raw/master/binaries/freebsd/amd64/goperf) 16 | 17 | [FreeBSD 32 bit](https://github.com/gnulnx/goperf/raw/master/binaries/freebsd/386/goperf) 18 | 19 | [Linux 64 bit](https://github.com/gnulnx/goperf/raw/master/binaries/linux/amd64/goperf) 20 | 21 | [Linux 32 bit](https://github.com/gnulnx/goperf/raw/master/binaries/linux/386/goperf) 22 | 23 | [Windows 64 bit](https://github.com/gnulnx/goperf/raw/master/binaries/windows/amd64/goperf.exe) 24 | 25 | [Windows 32 bit](https://github.com/gnulnx/goperf/raw/master/binaries/windows/386/goperf.exe) 26 | 27 | ## Usage: 28 | 29 | ### Fetch a page and display info. 30 | ``` 31 | ./goperf -url {url} -fetch 32 | ``` 33 | This will print output like: 34 | 35 | ![Alt text](readme_imgs/Fetch.png?raw=true "Fetch") 36 | 37 | To Fetch a page and display all it's assets use: 38 | ``` 39 | ./goperf -url {url} -fetch --printjson 40 | ``` 41 | **NOTE** this will print the content of the body in each of the fetched assets. If you have large minified JS bundles it will be pretty messy. *A future version will support only showing the body text* 42 | 43 | 44 | Fetch a page that requires a session id (such as a django login) 45 | ``` 46 | ./goperf -url http://192.168.33.11/student/ -fetch -cookies "sessionid_vagrant=0xkfeev882n0i9efkiq7vmd2i6efufz9;" --printjson 47 | ``` 48 | 49 | ### Load testing 50 | 51 | Tell goperf the number of users you want to simulate and the number of seconds you want the simulation to run. 52 | 53 | ``` 54 | ./goperf -url {url} -users {int} -sec {int} 55 | ``` 56 | 57 | Goperf will kick off a seperate go routine for each user. Each user will then continiously fetch the url along with all it's page assets in seperate go routines. *Each users will make an initial GET request to fetch the cookies and then use them in follow up requests in order to simulate users sessions.* 58 | 59 | The light weight nature of goroutines allows this high concurancy to simulate many users with very litte memory. You will most likely overhewlm the test url servers or consume all of the available network bandwidth before memory becomes an issue. 60 | 61 | Load testing results: 62 | 63 | ![Alt text](readme_imgs/GoPerfOutput.png?raw=true "Output") 64 | 65 | ## Setup 66 | #### Ensure gopath is correctly setup 67 | 68 | Make sure you have your GOPATH setup to point to the go/bin directory. 69 | If you have a default go install on ubuntu it would be ~/go/bin. 70 | If so you would add this to your path. 71 | ``` 72 | export PATH=$PATH:~/go/bin 73 | ``` 74 | #### Install 75 | 76 | ``` 77 | go get github.com/gnulnx/goperf 78 | ``` 79 | 80 | #### Build 81 | ``` 82 | go install github.com/gnulnx/goperf 83 | ``` 84 | 85 | 86 | ### Run minimal unit and benchmark tests 87 | ``` 88 | go test ./... -cover -bench=. 89 | ``` 90 | 91 | 92 | ## Road map and future plans. 93 | 94 | Currently goperf is quite good at simulating browser requests that include the body, css, img, and js assets. 95 | 96 | However goper has no concept of an ajax request. 97 | 98 | The next phase of goperf will be adding in support for additional requests after intial page load. For example say you wanted to time how long it took for 10 users to hit your website and also request a specific api. This approach will allow us to have much better simulation for javacsript heavy sites. 99 | 100 | Longer term support for a chaos mode where the perf "users" move through the site randomly selecting a new url after each request. 101 | -------------------------------------------------------------------------------- /binaries/darwin/amd64/goperf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnulnx/goperf/d7e65ae890a4d241d7538a3113590b70dbc8577a/binaries/darwin/amd64/goperf -------------------------------------------------------------------------------- /binaries/freebsd/386/goperf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnulnx/goperf/d7e65ae890a4d241d7538a3113590b70dbc8577a/binaries/freebsd/386/goperf -------------------------------------------------------------------------------- /binaries/freebsd/amd64/goperf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnulnx/goperf/d7e65ae890a4d241d7538a3113590b70dbc8577a/binaries/freebsd/amd64/goperf -------------------------------------------------------------------------------- /binaries/linux/386/goperf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnulnx/goperf/d7e65ae890a4d241d7538a3113590b70dbc8577a/binaries/linux/386/goperf -------------------------------------------------------------------------------- /binaries/linux/amd64/goperf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnulnx/goperf/d7e65ae890a4d241d7538a3113590b70dbc8577a/binaries/linux/amd64/goperf -------------------------------------------------------------------------------- /binaries/windows/386/goperf.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnulnx/goperf/d7e65ae890a4d241d7538a3113590b70dbc8577a/binaries/windows/386/goperf.exe -------------------------------------------------------------------------------- /binaries/windows/amd64/goperf.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnulnx/goperf/d7e65ae890a4d241d7538a3113590b70dbc8577a/binaries/windows/amd64/goperf.exe -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #/usr/bin/env bash 2 | 3 | # Build for WINDOWS 4 | env GOOS=windows GOARCH=amd64 go build 5 | mv goperf.exe binaries/windows/amd64 6 | 7 | env GOOS=windows GOARCH=386 go build 8 | mv goperf.exe binaries/windows/386 9 | 10 | # Build for FreeBSD 11 | env GOOS=freebsd GOARCH=amd64 go build 12 | mv goperf binaries/freebsd/amd64 13 | 14 | env GOOS=freebsd GOARCH=386 go build 15 | mv goperf binaries/freebsd/386/ 16 | 17 | env GOOS=darwin GOARCH=amd64 go build 18 | mv goperf binaries/darwin/amd64/ 19 | 20 | # Build for Linux 21 | env GOOS=linux GOARCH=386 go build 22 | mv goperf binaries/linux/386/ 23 | 24 | env GOOS=linux GOARCH=amd64 go build 25 | mv goperf binaries/linux/amd64/ 26 | 27 | # Build for current platform 28 | go build 29 | -------------------------------------------------------------------------------- /goperf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnulnx/goperf/d7e65ae890a4d241d7538a3113590b70dbc8577a/goperf -------------------------------------------------------------------------------- /goperf.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package goperf is a highly concurrant website load tester with a simple intuitive command line syntax. 3 | 4 | * Fetch a url and report stats 5 | 6 | This command will return all information for a given url. 7 | ./goperf -url http://qa.teaquinox.com -fetchall -printjson 8 | 9 | When fetchall is provided the returned struct will contain 10 | url, time, size, and data info. 11 | 12 | You can do a simpler request that leaves the data and headers out like this 13 | ./goperf -url http://qa.teaquinox.com -fetchall -printjson 14 | 15 | 16 | * Load testing 17 | ./goperf -url http://qa.teaquinox.com -sec 5 -users 5 18 | */ 19 | package main 20 | 21 | import ( 22 | "encoding/json" 23 | "flag" 24 | "fmt" 25 | "io" 26 | "net/http" 27 | "os" 28 | "runtime/pprof" 29 | "strconv" 30 | 31 | "github.com/gnulnx/color" 32 | "github.com/gnulnx/goperf/perf" 33 | "github.com/gnulnx/goperf/request" 34 | "github.com/gnulnx/vestigo" 35 | ) 36 | 37 | func main() { 38 | // I ❤️ the way go handles command line arguments 39 | fetch := flag.Bool("fetch", false, "Fetch -url and report it's stats. Does not return resources") 40 | fetchall := flag.Bool("fetchall", false, "Fetch -url and report stats return all assets (js, css, img)") 41 | printjson := flag.Bool("printjson", false, "Print json output") 42 | perftest := flag.Bool("perftest", false, "Run the goland perf suite") 43 | users := flag.Int("users", 1, "Number of concurrent users/connections") 44 | url := flag.String("url", "https://qa.teaquinox.com", "url to test") 45 | seconds := flag.Int("sec", 2, "Number of seconds each concurrant user/connection should make consequitive requests") 46 | web := flag.Bool("web", false, "Run as a webserver -web {port}") 47 | port := flag.Int("port", 8080, "used with -web to specif which port to bind") 48 | cookies := flag.String("cookies", "{}", "Set up cookies for the request") 49 | headers := flag.String("headers", "{}", "Set up headers for the request") 50 | useragent := flag.String("useragent", "goperf", "Set the user agent string") 51 | 52 | // Not currently used, but could be 53 | iterations := flag.Int("iter", 1000, "Iterations per user/connection") 54 | output := flag.Int("output", 5, "Show user output every {n} iterations") 55 | verbose := flag.Bool("verbose", false, "Show verbose output") 56 | var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") 57 | flag.Parse() 58 | 59 | http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 100 60 | 61 | if *web { 62 | router := vestigo.NewRouter() 63 | router.SetGlobalCors(&vestigo.CorsAccessControl{ 64 | AllowOrigin: []string{"*", "http://138.197.97.39:8080"}, 65 | }) 66 | 67 | router.Post("/api/", handler) 68 | router.SetCors("/api/", &vestigo.CorsAccessControl{ 69 | AllowMethods: []string{"POST"}, // only allow cors for this resource on POST calls 70 | }) 71 | sPort := ":" + strconv.Itoa(*port) 72 | color.Green("Your website is available at 127.0.0.1%s", sPort) 73 | http.ListenAndServe(sPort, router) 74 | } 75 | 76 | if *fetch || *fetchall { 77 | // TODO This method treats these command line arguments exactly the same... no good 78 | // -fetch -printjson should ONLY return the body of the primary request and not the other assets 79 | 80 | // This section will make an initial GET request and try to set any cookies we find 81 | if *cookies == "" { 82 | resp1, _ := http.Get(*url) 83 | if len(resp1.Header["Set-Cookie"]) > 0 { 84 | cookies = &resp1.Header["Set-Cookie"][0] 85 | } 86 | } 87 | resp := request.FetchAll( 88 | request.FetchInput{ 89 | BaseURL: *url, 90 | Retdat: *fetchall, 91 | Cookies: *cookies, 92 | Headers: *headers, 93 | UserAgent: *useragent, 94 | }, 95 | ) 96 | 97 | if *printjson { 98 | tmp, _ := json.MarshalIndent(resp, "", " ") 99 | fmt.Println(string(tmp)) 100 | } 101 | 102 | request.PrintFetchAllResponse(resp) 103 | 104 | os.Exit(1) 105 | } 106 | 107 | // TODO Declare an inline parameter struct... 108 | perfJob := &perf.Init{ 109 | Iterations: *iterations, 110 | Threads: *users, 111 | URL: *url, 112 | Output: *output, 113 | Verbose: *verbose, 114 | Seconds: *seconds, 115 | Cookies: *cookies, 116 | Headers: *headers, 117 | UserAgent: *useragent, 118 | } 119 | f, _ := os.Create(*cpuprofile) 120 | results := perfJob.Basic() 121 | 122 | if *perftest { 123 | pprof.StartCPUProfile(f) 124 | defer pprof.StopCPUProfile() 125 | } 126 | 127 | // Write json response to file. 128 | outfile, _ := os.Create("./output.json") 129 | 130 | if *printjson { 131 | perfJob.JsonResults() 132 | } else { 133 | perfJob.Print() 134 | } 135 | 136 | tmp, _ := json.MarshalIndent(results, "", " ") 137 | outfile.WriteString(string(tmp)) 138 | color.Magenta("Job Results Saved: ./output.json") 139 | } 140 | 141 | /* 142 | Check that the request parameters are correct and return them. 143 | Also return an array of error string if the parameters were not right 144 | */ 145 | func checkParams(r *http.Request) ([]string, string, int, int) { 146 | errors := []string{} 147 | seconds := 0 148 | users := 0 149 | var err error 150 | 151 | // Check that url has been supplied 152 | url, ok := r.PostForm["url"] 153 | if !ok { 154 | errors = append(errors, " - url (string) is a required field") 155 | url = []string{""} 156 | } 157 | 158 | // Check that seconds is supplied 159 | strSeconds, ok := r.PostForm["sec"] 160 | if !ok { 161 | errors = append(errors, " - sec (int) is a required field") 162 | strSeconds = []string{} 163 | } 164 | if len(strSeconds) > 0 { 165 | seconds, err = strconv.Atoi(strSeconds[0]) 166 | if err != nil { 167 | errors = append(errors, " - sec (int) is a required field") 168 | seconds = 0 169 | } 170 | } 171 | 172 | // Check user field has been supplied 173 | strUsers, ok := r.PostForm["users"] 174 | if !ok { 175 | errors = append(errors, " - users (int) is a required field") 176 | strUsers = []string{} 177 | } 178 | if len(strUsers) > 0 { 179 | users, err = strconv.Atoi(strUsers[0]) 180 | if err != nil { 181 | errors = append(errors, " - users (int) is a required field") 182 | users = 0 183 | } 184 | } 185 | 186 | return errors, url[0], seconds, users 187 | } 188 | 189 | func handler(w http.ResponseWriter, r *http.Request) { 190 | r.ParseForm() 191 | errors, url, seconds, users := checkParams(r) 192 | if len(errors) > 0 { 193 | for i := 0; i < len(errors); i++ { 194 | e := errors[i] + "\n" 195 | w.Write([]byte(e)) 196 | } 197 | return 198 | } 199 | 200 | perfJob := &perf.Init{ 201 | URL: url, 202 | Threads: users, 203 | Seconds: seconds, 204 | } 205 | perfJob.Basic() 206 | jsonResults := perfJob.JsonResults() 207 | 208 | w.Header().Set("Content-Type", "application/json") 209 | w.WriteHeader(http.StatusCreated) 210 | io.WriteString(w, jsonResults) 211 | } 212 | -------------------------------------------------------------------------------- /httputils/httputils.go: -------------------------------------------------------------------------------- 1 | package httputils 2 | 3 | import ( 4 | "log" 5 | "regexp" 6 | "strings" 7 | 8 | "github.com/PuerkitoBio/goquery" 9 | ) 10 | 11 | /* 12 | ParseAllAssetsSequential takes a string of text (typically from a http.Response.Body) 13 | and return the urls for the page 292 | unnamed 293 | 294 | 295 | cluster_L 296 | 297 | 298 | 299 | 300 | Type: cpu 301 | 302 | Type: cpu 303 | Time: Jan 18, 2018 at 2:41pm (EST) 304 | Duration: 1.20s, Total samples = 2.76s (229.41%) 305 | Showing nodes accounting for 2.71s, 98.19% of 2.76s total 306 | Dropped 21 nodes (cum <= 0.01s) 307 | 308 | 309 | 310 | N1 311 | 312 | 313 | runtime 314 | mach_semaphore_signal 315 | sys_darwin_amd64.s 316 | 2.38s (86.23%) 317 | 318 | 319 | 320 | 321 | 322 | N2 323 | 324 | 325 | runtime 326 | chansend1 327 | chan.go 328 | 0 of 2.38s (86.23%) 329 | 330 | 331 | 332 | 333 | 334 | N38 335 | 336 | 337 | runtime 338 | chansend 339 | chan.go 340 | 0 of 2.38s (86.23%) 341 | 342 | 343 | 344 | 345 | 346 | N2->N38 347 | 348 | 349 | 350 | 351 | 352 | 353 | 2.38s 354 | 355 | 356 | 357 | 358 | 359 | N3 360 | 361 | 362 | runtime 363 | systemstack 364 | asm_amd64.s 365 | 0.01s (0.36%) 366 | of 2.54s (92.03%) 367 | 368 | 369 | 370 | 371 | 372 | N42 373 | 374 | 375 | runtime 376 | goready 377 | func1 378 | proc.go 379 | 0 of 2.38s (86.23%) 380 | 381 | 382 | 383 | 384 | 385 | N3->N42 386 | 387 | 388 | 389 | 390 | 391 | 392 | 2.38s 393 | 394 | 395 | 396 | 397 | 398 | N54 399 | 400 | 401 | runtime 402 | semasleep 403 | func1 404 | os_darwin.go 405 | 0 of 0.13s (4.71%) 406 | 407 | 408 | 409 | 410 | 411 | N3->N54 412 | 413 | 414 | 415 | 416 | 417 | 418 | 0.13s 419 | 420 | 421 | 422 | 423 | 424 | N4 425 | 426 | 427 | github 428 | com/gnulnx/goperf/httputils 429 | Resources 430 | func1 431 | httputils.go 432 | 0 of 1.03s (37.32%) 433 | 434 | 435 | 436 | 437 | 438 | N4->N2 439 | 440 | 441 | 442 | 443 | 444 | 445 | 0.96s 446 | 447 | 448 | 449 | 450 | 451 | N27 452 | 453 | 454 | github 455 | com/gnulnx/goperf/httputils 456 | Getjs 457 | httputils.go 458 | 0 of 0.07s (2.54%) 459 | 460 | 461 | 462 | 463 | 464 | N4->N27 465 | 466 | 467 | 468 | 469 | 470 | 471 | 0.07s 472 | 473 | 474 | 475 | 476 | 477 | N5 478 | 479 | 480 | github 481 | com/gnulnx/goperf/httputils 482 | Resources 483 | func2 484 | httputils.go 485 | 0 of 0.77s (27.90%) 486 | 487 | 488 | 489 | 490 | 491 | N5->N2 492 | 493 | 494 | 495 | 496 | 497 | 498 | 0.74s 499 | 500 | 501 | 502 | 503 | 504 | N26 505 | 506 | 507 | github 508 | com/gnulnx/goperf/httputils 509 | Getimg 510 | httputils.go 511 | 0 of 0.03s (1.09%) 512 | 513 | 514 | 515 | 516 | 517 | N5->N26 518 | 519 | 520 | 521 | 522 | 523 | 524 | 0.03s 525 | 526 | 527 | 528 | 529 | 530 | N6 531 | 532 | 533 | github 534 | com/gnulnx/goperf/httputils 535 | Resources 536 | func3 537 | httputils.go 538 | 0 of 0.70s (25.36%) 539 | 540 | 541 | 542 | 543 | 544 | N6->N2 545 | 546 | 547 | 548 | 549 | 550 | 551 | 0.68s 552 | 553 | 554 | 555 | 556 | 557 | N25 558 | 559 | 560 | github 561 | com/gnulnx/goperf/httputils 562 | Getcss 563 | httputils.go 564 | 0 of 0.02s (0.72%) 565 | 566 | 567 | 568 | 569 | 570 | N6->N25 571 | 572 | 573 | 574 | 575 | 576 | 577 | 0.02s 578 | 579 | 580 | 581 | 582 | 583 | N7 584 | 585 | 586 | github 587 | com/gnulnx/goperf/httputils 588 | runregex 589 | httputils.go 590 | 0 of 0.12s (4.35%) 591 | 592 | 593 | 594 | 595 | 596 | N28 597 | 598 | 599 | regexp 600 | (*Regexp) 601 | FindAllStringSubmatch 602 | regexp.go 603 | 0 of 0.05s (1.81%) 604 | 605 | 606 | 607 | 608 | 609 | N7->N28 610 | 611 | 612 | 613 | 614 | 615 | 616 | 0.05s 617 | 618 | 619 | 620 | 621 | 622 | N32 623 | 624 | 625 | regexp 626 | Compile 627 | regexp.go 628 | 0 of 0.07s (2.54%) 629 | 630 | 631 | 632 | 633 | 634 | N7->N32 635 | 636 | 637 | 638 | 639 | 640 | 641 | 0.07s 642 | 643 | 644 | 645 | 646 | 647 | N8 648 | 649 | 650 | runtime 651 | kevent 652 | sys_darwin_amd64.s 653 | 0.13s (4.71%) 654 | 655 | 656 | 657 | 658 | 659 | N9 660 | 661 | 662 | runtime 663 | mcall 664 | asm_amd64.s 665 | 0 of 0.21s (7.61%) 666 | 667 | 668 | 669 | 670 | 671 | N51 672 | 673 | 674 | runtime 675 | park_m 676 | proc.go 677 | 0 of 0.21s (7.61%) 678 | 679 | 680 | 681 | 682 | 683 | N9->N51 684 | 685 | 686 | 687 | 688 | 689 | 690 | 0.21s 691 | 692 | 693 | 694 | 695 | 696 | N10 697 | 698 | 699 | runtime 700 | findrunnable 701 | proc.go 702 | 0 of 0.21s (7.61%) 703 | 704 | 705 | 706 | 707 | 708 | N18 709 | 710 | 711 | runtime 712 | stopm 713 | proc.go 714 | 0 of 0.10s (3.62%) 715 | 716 | 717 | 718 | 719 | 720 | N10->N18 721 | 722 | 723 | 724 | 725 | 726 | 727 | 0.08s 728 | 729 | 730 | 731 | 732 | 733 | N46 734 | 735 | 736 | runtime 737 | netpoll 738 | netpoll_kqueue.go 739 | 0 of 0.13s (4.71%) 740 | 741 | 742 | 743 | 744 | 745 | N10->N46 746 | 747 | 748 | 749 | 750 | 751 | 752 | 0.13s 753 | 754 | 755 | 756 | 757 | 758 | N11 759 | 760 | 761 | runtime 762 | mach_semaphore_wait 763 | sys_darwin_amd64.s 764 | 0.10s (3.62%) 765 | 766 | 767 | 768 | 769 | 770 | N12 771 | 772 | 773 | runtime 774 | schedule 775 | proc.go 776 | 0 of 0.23s (8.33%) 777 | 778 | 779 | 780 | 781 | 782 | N12->N10 783 | 784 | 785 | 786 | 787 | 788 | 789 | 0.21s 790 | 791 | 792 | 793 | 794 | 795 | N39 796 | 797 | 798 | runtime 799 | gcstopm 800 | proc.go 801 | 0 of 0.02s (0.72%) 802 | 803 | 804 | 805 | 806 | 807 | N12->N39 808 | 809 | 810 | 811 | 812 | 813 | 814 | 0.02s 815 | 816 | 817 | 818 | 819 | 820 | N13 821 | 822 | 823 | runtime 824 | mallocgc 825 | malloc.go 826 | 0 of 0.07s (2.54%) 827 | 828 | 829 | 830 | 831 | 832 | N16 833 | 834 | 835 | runtime 836 | memclrNoHeapPointers 837 | memclr_amd64.s 838 | 0.04s (1.45%) 839 | 840 | 841 | 842 | 843 | 844 | N13->N16 845 | 846 | 847 | 848 | 849 | 850 | 851 | 0.04s 852 | 853 | 854 | 855 | 856 | 857 | N22 858 | 859 | 860 | runtime 861 | gcAssistAlloc 862 | mgcmark.go 863 | 0 of 0.02s (0.72%) 864 | 865 | 866 | 867 | 868 | 869 | N13->N22 870 | 871 | 872 | 873 | 874 | 875 | 876 | 0.02s 877 | 878 | 879 | 880 | 881 | 882 | N14 883 | 884 | 885 | runtime 886 | semasleep1 887 | os_darwin.go 888 | 0 of 0.13s (4.71%) 889 | 890 | 891 | 892 | 893 | 894 | N14->N11 895 | 896 | 897 | 898 | 899 | 900 | 901 | 0.10s 902 | 903 | 904 | 905 | 906 | 907 | N19 908 | 909 | 910 | runtime 911 | mach_semaphore_timedwait 912 | sys_darwin_amd64.s 913 | 0.03s (1.09%) 914 | 915 | 916 | 917 | 918 | 919 | N14->N19 920 | 921 | 922 | 923 | 924 | 925 | 926 | 0.03s 927 | 928 | 929 | 930 | 931 | 932 | N15 933 | 934 | 935 | regexp 936 | compile 937 | regexp.go 938 | 0 of 0.07s (2.54%) 939 | 940 | 941 | 942 | 943 | 944 | N36 945 | 946 | 947 | regexp/syntax 948 | Compile 949 | compile.go 950 | 0 of 0.02s (0.72%) 951 | 952 | 953 | 954 | 955 | 956 | N15->N36 957 | 958 | 959 | 960 | 961 | 962 | 963 | 0.02s 964 | 965 | 966 | 967 | 968 | 969 | N37 970 | 971 | 972 | regexp/syntax 973 | Parse 974 | parse.go 975 | 0 of 0.04s (1.45%) 976 | 977 | 978 | 979 | 980 | 981 | N15->N37 982 | 983 | 984 | 985 | 986 | 987 | 988 | 0.04s 989 | 990 | 991 | 992 | 993 | 994 | N17 995 | 996 | 997 | regexp 998 | (*machine) 999 | add 1000 | exec.go 1001 | 0.02s (0.72%) 1002 | of 0.03s (1.09%) 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | N17->N3 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 0.01s 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | N49 1022 | 1023 | 1024 | runtime 1025 | notesleep 1026 | lock_sema.go 1027 | 0 of 0.10s (3.62%) 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | N18->N49 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 0.10s 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | N20 1047 | 1048 | 1049 | regexp 1050 | (*machine) 1051 | match 1052 | exec.go 1053 | 0 of 0.04s (1.45%) 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | N20->N17 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 0.01s 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | N31 1073 | 1074 | 1075 | regexp 1076 | (*machine) 1077 | step 1078 | exec.go 1079 | 0 of 0.02s (0.72%) 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | N20->N31 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 0.02s 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | N21 1099 | 1100 | 1101 | runtime 1102 | gcMarkDone 1103 | mgc.go 1104 | 0 of 0.03s (1.09%) 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | N21->N3 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 0.03s 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | N22->N3 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 0.01s 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | N22->N21 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 0.01s 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | N23 1150 | 1151 | 1152 | runtime 1153 | gcBgMarkWorker 1154 | mgc.go 1155 | 0 of 0.02s (0.72%) 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | N23->N21 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 0.02s 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | N24 1175 | 1176 | 1177 | runtime 1178 | morestack 1179 | asm_amd64.s 1180 | 0 of 0.02s (0.72%) 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | N48 1187 | 1188 | 1189 | runtime 1190 | newstack 1191 | stack.go 1192 | 0 of 0.02s (0.72%) 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | N24->N48 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 0.02s 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | N25->N7 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 0.02s 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | N26->N7 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 0.03s 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | N27->N7 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 0.07s 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | N29 1251 | 1252 | 1253 | regexp 1254 | (*Regexp) 1255 | allMatches 1256 | regexp.go 1257 | 0 of 0.05s (1.81%) 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | N28->N29 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 0.05s 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | N30 1277 | 1278 | 1279 | regexp 1280 | (*Regexp) 1281 | doExecute 1282 | exec.go 1283 | 0 of 0.05s (1.81%) 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | N29->N30 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 0.05s 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | N30->N20 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 0.04s 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | N31->N17 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 0.02s 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | N32->N15 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 0.07s 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | N33 1342 | 1343 | 1344 | regexp/syntax 1345 | (*compiler) 1346 | compile 1347 | compile.go 1348 | 0 of 0.02s (0.72%) 1349 | 1350 | 1351 | 1352 | 1353 | 1354 | N35 1355 | 1356 | 1357 | regexp/syntax 1358 | (*compiler) 1359 | rune 1360 | compile.go 1361 | 0 of 0.02s (0.72%) 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | N33->N35 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | 1374 | 0.02s 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | N34 1381 | 1382 | 1383 | regexp/syntax 1384 | (*compiler) 1385 | inst 1386 | compile.go 1387 | 0 of 0.02s (0.72%) 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | N44 1394 | 1395 | 1396 | runtime 1397 | growslice 1398 | slice.go 1399 | 0 of 0.02s (0.72%) 1400 | 1401 | 1402 | 1403 | 1404 | 1405 | N34->N44 1406 | 1407 | 1408 | 1409 | 1410 | 1411 | 1412 | 0.02s 1413 | 1414 | 1415 | 1416 | 1417 | 1418 | N35->N34 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | 0.02s 1426 | (inline) 1427 | 1428 | 1429 | 1430 | 1431 | 1432 | N36->N33 1433 | 1434 | 1435 | 1436 | 1437 | 1438 | 1439 | 0.02s 1440 | 1441 | 1442 | 1443 | 1444 | 1445 | N47 1446 | 1447 | 1448 | runtime 1449 | newobject 1450 | malloc.go 1451 | 0 of 0.04s (1.45%) 1452 | 1453 | 1454 | 1455 | 1456 | 1457 | N37->N47 1458 | 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 0.04s 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | N56 1471 | 1472 | 1473 | runtime 1474 | send 1475 | chan.go 1476 | 0 of 2.38s (86.23%) 1477 | 1478 | 1479 | 1480 | 1481 | 1482 | N38->N56 1483 | 1484 | 1485 | 1486 | 1487 | 1488 | 1489 | 2.38s 1490 | 1491 | 1492 | 1493 | 1494 | 1495 | N39->N18 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | 1502 | 0.02s 1503 | 1504 | 1505 | 1506 | 1507 | 1508 | N40 1509 | 1510 | 1511 | runtime 1512 | gopreempt_m 1513 | proc.go 1514 | 0 of 0.02s (0.72%) 1515 | 1516 | 1517 | 1518 | 1519 | 1520 | N43 1521 | 1522 | 1523 | runtime 1524 | goschedImpl 1525 | proc.go 1526 | 0 of 0.02s (0.72%) 1527 | 1528 | 1529 | 1530 | 1531 | 1532 | N40->N43 1533 | 1534 | 1535 | 1536 | 1537 | 1538 | 1539 | 0.02s 1540 | 1541 | 1542 | 1543 | 1544 | 1545 | N41 1546 | 1547 | 1548 | runtime 1549 | goready 1550 | proc.go 1551 | 0 of 2.38s (86.23%) 1552 | 1553 | 1554 | 1555 | 1556 | 1557 | N41->N3 1558 | 1559 | 1560 | 1561 | 1562 | 1563 | 1564 | 2.38s 1565 | 1566 | 1567 | 1568 | 1569 | 1570 | N52 1571 | 1572 | 1573 | runtime 1574 | ready 1575 | proc.go 1576 | 0 of 2.38s (86.23%) 1577 | 1578 | 1579 | 1580 | 1581 | 1582 | N42->N52 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | 1589 | 2.38s 1590 | 1591 | 1592 | 1593 | 1594 | 1595 | N43->N12 1596 | 1597 | 1598 | 1599 | 1600 | 1601 | 1602 | 0.02s 1603 | 1604 | 1605 | 1606 | 1607 | 1608 | N44->N13 1609 | 1610 | 1611 | 1612 | 1613 | 1614 | 1615 | 0.02s 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | N45 1622 | 1623 | 1624 | runtime 1625 | mach_semrelease 1626 | os_darwin.go 1627 | 0 of 2.38s (86.23%) 1628 | 1629 | 1630 | 1631 | 1632 | 1633 | N45->N1 1634 | 1635 | 1636 | 1637 | 1638 | 1639 | 1640 | 2.38s 1641 | 1642 | 1643 | 1644 | 1645 | 1646 | N46->N8 1647 | 1648 | 1649 | 1650 | 1651 | 1652 | 1653 | 0.13s 1654 | 1655 | 1656 | 1657 | 1658 | 1659 | N47->N13 1660 | 1661 | 1662 | 1663 | 1664 | 1665 | 1666 | 0.04s 1667 | 1668 | 1669 | 1670 | 1671 | 1672 | N48->N40 1673 | 1674 | 1675 | 1676 | 1677 | 1678 | 1679 | 0.02s 1680 | 1681 | 1682 | 1683 | 1684 | 1685 | N53 1686 | 1687 | 1688 | runtime 1689 | semasleep 1690 | os_darwin.go 1691 | 0 of 0.10s (3.62%) 1692 | 1693 | 1694 | 1695 | 1696 | 1697 | N49->N53 1698 | 1699 | 1700 | 1701 | 1702 | 1703 | 1704 | 0.10s 1705 | 1706 | 1707 | 1708 | 1709 | 1710 | N50 1711 | 1712 | 1713 | runtime 1714 | notewakeup 1715 | lock_sema.go 1716 | 0 of 2.38s (86.23%) 1717 | 1718 | 1719 | 1720 | 1721 | 1722 | N55 1723 | 1724 | 1725 | runtime 1726 | semawakeup 1727 | os_darwin.go 1728 | 0 of 2.38s (86.23%) 1729 | 1730 | 1731 | 1732 | 1733 | 1734 | N50->N55 1735 | 1736 | 1737 | 1738 | 1739 | 1740 | 1741 | 2.38s 1742 | 1743 | 1744 | 1745 | 1746 | 1747 | N51->N12 1748 | 1749 | 1750 | 1751 | 1752 | 1753 | 1754 | 0.21s 1755 | 1756 | 1757 | 1758 | 1759 | 1760 | N58 1761 | 1762 | 1763 | runtime 1764 | wakep 1765 | proc.go 1766 | 0 of 2.38s (86.23%) 1767 | 1768 | 1769 | 1770 | 1771 | 1772 | N52->N58 1773 | 1774 | 1775 | 1776 | 1777 | 1778 | 1779 | 2.38s 1780 | 1781 | 1782 | 1783 | 1784 | 1785 | N53->N3 1786 | 1787 | 1788 | 1789 | 1790 | 1791 | 1792 | 0.10s 1793 | 1794 | 1795 | 1796 | 1797 | 1798 | N54->N14 1799 | 1800 | 1801 | 1802 | 1803 | 1804 | 1805 | 0.13s 1806 | 1807 | 1808 | 1809 | 1810 | 1811 | N55->N45 1812 | 1813 | 1814 | 1815 | 1816 | 1817 | 1818 | 2.38s 1819 | 1820 | 1821 | 1822 | 1823 | 1824 | N56->N41 1825 | 1826 | 1827 | 1828 | 1829 | 1830 | 1831 | 2.38s 1832 | 1833 | 1834 | 1835 | 1836 | 1837 | N57 1838 | 1839 | 1840 | runtime 1841 | startm 1842 | proc.go 1843 | 0 of 2.38s (86.23%) 1844 | 1845 | 1846 | 1847 | 1848 | 1849 | N57->N50 1850 | 1851 | 1852 | 1853 | 1854 | 1855 | 1856 | 2.38s 1857 | 1858 | 1859 | 1860 | 1861 | 1862 | N58->N57 1863 | 1864 | 1865 | 1866 | 1867 | 1868 | 1869 | 2.38s 1870 | 1871 | 1872 | 1873 | 1874 | 1875 | -------------------------------------------------------------------------------- /httputils/test_data/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TeaQuinox | Buy Loose Leaf Tea, Green Tea, Black Tea, White Tea, and Tisanes!

Join our email list and receive free shipping.

{{email}}

Loose Leaf Teas

Loose Leaf Black Tea

Loose Leaf Green Tea