From 00b8b5d29de446825c1f18968b165be61c5f9bac Mon Sep 17 00:00:00 2001 From: Robert Cowham Date: Tue, 24 Oct 2023 16:00:39 +0100 Subject: [PATCH] Add in missing tables and improve UI in output --- cmd/p4locks/main.go | 99 ++++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/cmd/p4locks/main.go b/cmd/p4locks/main.go index 353dd43..94d0e96 100644 --- a/cmd/p4locks/main.go +++ b/cmd/p4locks/main.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "bytes" "compress/gzip" "context" "encoding/json" @@ -12,6 +13,7 @@ import ( "regexp" "strings" "sync" + "text/template" "time" "github.com/pkg/profile" @@ -78,7 +80,7 @@ func readerFromFile(file *os.File) (io.Reader, int64, error) { } // chart header followed by data records -func writeHeader(f *bufio.Writer) error { +func writeHeader(f *bufio.Writer, thresholdFilter int64) error { header := ` @@ -100,8 +102,8 @@ func writeHeader(f *bufio.Writer) error {
- - + +
@@ -117,12 +119,18 @@ func writeHeader(f *bufio.Writer) error { var base_data = [ ` - _, err := fmt.Fprint(f, header) + var buf bytes.Buffer + templ := template.Must(template.New("myname").Parse(header)) + templ.Execute(&buf, map[string]interface{}{ + "threshold": fmt.Sprintf("%d", thresholdFilter), + }) + + _, err := fmt.Fprint(f, buf.String()) return err } // chart trailer -func writeTrailer(f *bufio.Writer) error { +func writeTrailer(f *bufio.Writer, params string) error { trailer := ` ]; @@ -183,6 +191,10 @@ func writeTrailer(f *bufio.Writer) error { "db.storage", "db.storageg", "db.storagesx", + "db.storageup_R", + "db.storageup_W", + "db.storagemasterup_R", + "db.storagemasterup_W", "db.revdx", "db.revhx", "db.revpx", @@ -194,6 +206,7 @@ func writeTrailer(f *bufio.Writer) error { "db.rev", "db.revtx", "db.revstg", + "db.revfs", "db.locks", "db.locksg", "db.working", @@ -278,7 +291,6 @@ func writeTrailer(f *bufio.Writer) error { return d; // null function now - expects to be pass millis } - function humanize (milliseconds) { var seconds = Math.floor(milliseconds / 1000); var levels = [ @@ -406,7 +418,7 @@ func writeTrailer(f *bufio.Writer) error { var threshold = document.getElementById('txtThreshold').value; data = base_data.filter(item => perforceTableLockOrder.indexOf(item.Table) != -1); data = data.filter(item => item.MaxLock > threshold); - document.getElementById('txtSummary').innerHTML = 'Records - total: ' + base_data.length + ' filtered: ' + data.length; + document.getElementById('txtSummary').innerHTML = 'Records - total: ' + base_data.length + ' filtered: ' + data.length + ' ({{ .params }})'; data.sort(function(a, b){ var atable = perforceTableLockOrder.indexOf(a.Table); var btable = perforceTableLockOrder.indexOf(b.Table); @@ -420,6 +432,14 @@ func writeTrailer(f *bufio.Writer) error { chart.draw(processLockEvents(data), options); } + // Pressing Enter key in threshold textbox will cause button to be clicked + document.getElementById("txtThreshold").addEventListener("keyup", function(event) { + event.preventDefault(); + if (event.keyCode === 13) { + document.getElementById("btnChangeThreshold").click(); + } + }); + google.charts.load("current", {packages:["timeline"]}); google.charts.setOnLoadCallback(drawChart); @@ -428,7 +448,13 @@ func writeTrailer(f *bufio.Writer) error { ` - _, err := fmt.Fprint(f, trailer) + var buf bytes.Buffer + templ := template.Must(template.New("myname").Parse(trailer)) + templ.Execute(&buf, map[string]interface{}{ + "params": params, + }) + + _, err := fmt.Fprint(f, buf.String()) return err } @@ -487,17 +513,17 @@ type P4DLocks struct { countOutput int } -// { -// "Table": "db.revsx", -// "Pid": 72052, -// "Command": "user-sync -n //data/...", -// "User": "build", -// "Start": "2022-02-02T15:15:14Z", -// "Read": { -// "Wait": 0, -// "Held": 554000000 -// } -// } +// { +// "Table": "db.revsx", +// "Pid": 72052, +// "Command": "user-sync -n //data/...", +// "User": "build", +// "Start": "2022-02-02T15:15:14Z", +// "Read": { +// "Wait": 0, +// "Held": 554000000 +// } +// } func (pl *P4DLocks) writeCmd(f *bufio.Writer, cmd *p4dlog.Command) error { for _, t := range cmd.Tables { if pl.excludeTablesString != "" { @@ -706,7 +732,7 @@ func main() { var ( logfiles = kingpin.Arg( "logfile", - "Log files to process.").Strings() + "Log files to process (may be gzipped).").Strings() debug = kingpin.Flag( "debug", "Enable debugging level.", @@ -725,11 +751,21 @@ func main() { ).Short('x').String() ) kingpin.UsageTemplate(kingpin.CompactUsageTemplate).Version(version.Print("p4locks")).Author("Robert Cowham") - kingpin.CommandLine.Help = "Parses one or more p4d text log files (which may be gzipped) and outputs an HTML file with a Google Charts timeline with information about locks.\n" + - "Locks are listed by table and then pids with read/write wait/held.\n" + - "The output file can be opened locally by any browser (although internet access required to download JS).\n\n" + - "Examples:\n" + - "p4locks -x user log" + kingpin.CommandLine.Help = `Parses one or more p4d text log files (which may be gzipped) and outputs an HTML file with a Google Charts timeline with information about locks. +Locks are listed by table and then pids with read/write wait/held. +The output file can be opened locally by any browser (although internet access required to download JS). + +Usage examples: + +Exclude "db.user" table: + p4locks -x user log + +Use a lower default threshold (ms): + p4locks -t 1000 my.log + +Process multiple log files (gzipped or not) into single output file: + p4locks -o report.html log-2023-*.gz +` kingpin.HelpFlag.Short('h') kingpin.Parse() @@ -774,7 +810,7 @@ func main() { startTime := time.Now() logger.Infof("%v", version.Print("p4locks")) logger.Infof("Starting %s, Logfiles: %v", startTime, *logfiles) - logger.Infof("Flags: debug %v, htmlfile %v", *debug, *htmlOutputFile) + logger.Infof("Flags: debug %v, htmlfile %v, threshold (ms) %v", *debug, *htmlOutputFile, *threshold) linesChan := make(chan string, 10000) @@ -783,8 +819,7 @@ func main() { var fHTML *bufio.Writer var fdHTML *os.File - var htmlFilename string - htmlFilename = getHTMLFilename(*htmlOutputFile, *logfiles) + htmlFilename := getHTMLFilename(*htmlOutputFile, *logfiles) fdHTML, fHTML, err = openFile(htmlFilename) if err != nil { logger.Fatal(err) @@ -818,7 +853,7 @@ func main() { pl.processEvents(*logfiles) }() - err = writeHeader(fHTML) + err = writeHeader(fHTML, thresholdFilter) if err != nil { logger.Errorf("Failed to write header: %v", err) } @@ -833,12 +868,12 @@ func main() { fHTML.Flush() } } - err = writeTrailer(fHTML) + err = writeTrailer(fHTML, fmt.Sprintf("extraction threshold (ms): %d, excluded tables: %s", thresholdFilter, pl.excludeTablesString)) if err != nil { logger.Errorf("Failed to write trailer: %v", err) } wg.Wait() - logger.Infof("Completed %s, elapsed %s, cmds total %d", - time.Now(), time.Since(startTime), pl.countTotal) + logger.Infof("Completed %s, elapsed %s, cmds total %d, filtered output count %d", + time.Now(), time.Since(startTime), pl.countTotal, pl.countOutput) }