From 97fc9f3cbcc74276282852acd54ce4c31e326dfb Mon Sep 17 00:00:00 2001 From: Graham Clark Date: Sun, 15 Aug 2021 00:57:29 -0400 Subject: [PATCH] Various improvements to the progress bar With termshark v2.2, progress could be "jumpy" sometimes, flickering on and off again. Here is the intended behavior: - If PSML is being loaded, use the progress through the pcap file as the progress bar percentage - If PDML is being loaded and is "visible" i.e. the UI is waiting for packets in order to display them, use the ratio of the current length of the loaded 1000 packet batch to the packet number needed to populate the display as the progress bar percentage If the /proc support to determine progress through a file descriptor is missing on the target OS, a spinner is displayed instead. Note that if an interface, fifo or pipe is in use, the progress bar will be a spinner until/unless the read is interrupted; then will switch to progress until the PDML has been processed far enough in order to satisfy the UI's need for the packet struct and hex views. --- cmd/termshark/termshark.go | 77 ++++++++++++++++++++------------------ ui/ui.go | 55 ++++++++++++++++----------- 2 files changed, 74 insertions(+), 58 deletions(-) diff --git a/cmd/termshark/termshark.go b/cmd/termshark/termshark.go index 6775295..ead173e 100644 --- a/cmd/termshark/termshark.go +++ b/cmd/termshark/termshark.go @@ -991,11 +991,48 @@ Loop: // Only display the progress bar if PSML is loading or if PDML is loading that is needed // by the UI. If the PDML is an optimistic load out of the display, then no need for // progress. + doprog := false if ui.Loader.PsmlLoader.IsLoading() || (ui.Loader.PdmlLoader.IsLoading() && ui.Loader.PdmlLoader.LoadIsVisible()) { + if prevProgPercentage >= 1.0 { + if progCancelTimer != nil { + progCancelTimer.Reset(time.Duration(500) * time.Millisecond) + progCancelTimer = nil + } + } else { + ui.SetProgressWidget(app) + if progCancelTimer == nil { + progCancelTimer = time.AfterFunc(time.Duration(100000)*time.Hour, func() { + app.Run(gowid.RunFunction(func(app gowid.IApp) { + ui.ClearProgressWidget(app) + progCancelTimer = nil + })) + }) + } + } + tickChan = progTicker.C // progress is only enabled when a pcap may be loading - } else { - // Reset for the next load - prevProgPercentage = 0.0 + + // Rule: + // - prefer progress if we can apply it to psml only (not pdml) + // - otherwise use a spinner if interface load or fifo load in operation + // - otherwise use progress for pdml + if system.HaveFdinfo { + // Prefer progress, if the OS supports it. + doprog = true + if ui.Loader.ReadingFromFifo() { + // But if we are have an interface load (or a pipe load), then we can't + // predict when the data will run out, so use a spinner. That's because we + // feed the data to tshark -T psml with a tail command which reads from + // the tmp file being created by the pipe/interface source. + doprog = false + if !ui.Loader.InterfaceLoader.IsLoading() && !ui.Loader.PsmlLoader.IsLoading() { + // Unless those loads are finished, and the only loading activity is now + // PDML/pcap, which is loaded on demand in blocks of 1000. Then we can + // use the progress bar. + doprog = true + } + } + } } if ui.Loader.InterfaceLoader.IsLoading() { @@ -1192,40 +1229,6 @@ Loop: case <-tickChan: // We already know that we are LoadingPdml|LoadingPsml - ui.SetProgressWidget(app) - if progCancelTimer != nil { - progCancelTimer.Reset(time.Duration(500) * time.Millisecond) - } else { - progCancelTimer = time.AfterFunc(time.Duration(500)*time.Millisecond, func() { - app.Run(gowid.RunFunction(func(app gowid.IApp) { - ui.ClearProgressWidget(app) - })) - }) - } - - // Rule: - // - prefer progress if we can apply it to psml only (not pdml) - // - otherwise use a spinner if interface load or fifo load in operation - // - otherwise use progress for pdml - doprog := false - if system.HaveFdinfo { - // Prefer progress, if the OS supports it. - doprog = true - if ui.Loader.ReadingFromFifo() { - // But if we are have an interface load (or a pipe load), then we can't - // predict when the data will run out, so use a spinner. That's because we - // feed the data to tshark -T psml with a tail command which reads from - // the tmp file being created by the pipe/interface source. - doprog = false - if !ui.Loader.InterfaceLoader.IsLoading() && !ui.Loader.PsmlLoader.IsLoading() { - // Unless those loads are finished, and the only loading activity is now - // PDML/pcap, which is loaded on demand in blocks of 1000. Then we can - // use the progress bar. - doprog = true - } - } - } - if doprog { app.Run(gowid.RunFunction(func(app gowid.IApp) { prevProgPercentage = ui.UpdateProgressBarForFile(ui.Loader, prevProgPercentage, app) diff --git a/ui/ui.go b/ui/ui.go index 59a0854..4483b63 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -459,20 +459,27 @@ func UpdateProgressBarForFile(c *pcap.PacketLoader, prevRatio float64, app gowid // This shows where we are in the packet list. We want progress to be active only // as long as our view has missing widgets. So this can help predict when our little - // view into the list of packets will be populated. - currentRow := -1 - var currentRowMod int64 = -1 - var currentRowDiv int = -1 + // view into the list of packets will be populated. Note that if a new pcap is loading, + // the packet list view should always be further away than the last packet, so we won't + // need the progress bar to tell the user how long until packets appear in the packet + // list view; but the packet struct and hex views are populated using a different + // mechanism (separate tshark processes) and may leave their views blank while the + // packet list view shows data - so the progress bar is useful to indicate when info + // will show up in the struct and hex views. + currentDisplayedRow := -1 + var currentDisplayedRowMod int64 = -1 + var currentDisplayedRowDiv int = -1 if packetListView != nil { if fxy, err := packetListView.FocusXY(); err == nil { - foo, ok := packetListView.Model().RowIdentifier(fxy.Row) + currentRowId, ok := packetListView.Model().RowIdentifier(fxy.Row) if ok { pktsPerLoad := c.PacketsPerLoad() - currentRow = int(foo) - currentRowMod = int64(currentRow % pktsPerLoad) - currentRowDiv = (currentRow / pktsPerLoad) * pktsPerLoad + currentDisplayedRow = int(currentRowId) + currentDisplayedRowMod = int64(currentDisplayedRow % pktsPerLoad) + // Rounded to 1000 by default + currentDisplayedRowDiv = (currentDisplayedRow / pktsPerLoad) * pktsPerLoad c.PsmlLoader.Lock() - curRowProg.cur, curRowProg.max = int64(currentRow), int64(len(c.PsmlData())) + curRowProg.cur, curRowProg.max = int64(currentDisplayedRow), int64(len(c.PsmlData())) c.PsmlLoader.Unlock() } } @@ -481,12 +488,16 @@ func UpdateProgressBarForFile(c *pcap.PacketLoader, prevRatio float64, app gowid // Progress determined by how many of the (up to) pktsPerLoad pdml packets are read // If it's not the same chunk of rows, assume it won't affect our view, so no progress needed if c.PdmlLoader.IsLoading() { - if c.LoadingRow() == currentRowDiv { + if c.LoadingRow() == currentDisplayedRowDiv { + // Data being loaded from pdml + pcap may overlap the current view if x, err = c.LengthOfPdmlCacheEntry(c.LoadingRow()); err == nil { pdmlPacketProg.cur = int64(x) pdmlPacketProg.max = int64(c.KillAfterReadingThisMany) - if currentRow != -1 && currentRowMod < pdmlPacketProg.max { - pdmlPacketProg.max = currentRowMod + 1 // zero-based + if currentDisplayedRow != -1 && currentDisplayedRowMod < pdmlPacketProg.max { + pdmlPacketProg.max = currentDisplayedRowMod + 1 // zero-based + if pdmlPacketProg.cur > pdmlPacketProg.max { + pdmlPacketProg.cur = pdmlPacketProg.max + } } } @@ -496,7 +507,7 @@ func UpdateProgressBarForFile(c *pcap.PacketLoader, prevRatio float64, app gowid c.PdmlLoader.Unlock() if err == nil { pdmlIdxProg.cur, pdmlIdxProg.max = c2, m - if currentRow != -1 { + if currentDisplayedRow != -1 { // Only need to look this far into the psml file before my view is populated m = m * (curRowProg.cur / curRowProg.max) } @@ -506,8 +517,11 @@ func UpdateProgressBarForFile(c *pcap.PacketLoader, prevRatio float64, app gowid if x, err = c.LengthOfPcapCacheEntry(c.LoadingRow()); err == nil { pcapPacketProg.cur = int64(x) pcapPacketProg.max = int64(c.KillAfterReadingThisMany) - if currentRow != -1 && currentRowMod < pcapPacketProg.max { - pcapPacketProg.max = currentRowMod + 1 // zero-based + if currentDisplayedRow != -1 && currentDisplayedRowMod < pcapPacketProg.max { + pcapPacketProg.max = currentDisplayedRowMod + 1 // zero-based + if pcapPacketProg.cur > pcapPacketProg.max { + pcapPacketProg.cur = pcapPacketProg.max + } } } @@ -517,7 +531,7 @@ func UpdateProgressBarForFile(c *pcap.PacketLoader, prevRatio float64, app gowid c.PdmlLoader.Unlock() if err == nil { pcapIdxProg.cur, pcapIdxProg.max = c2, m - if currentRow != -1 { + if currentDisplayedRow != -1 { // Only need to look this far into the psml file before my view is populated m = m * (curRowProg.cur / curRowProg.max) } @@ -559,12 +573,11 @@ func UpdateProgressBarForFile(c *pcap.PacketLoader, prevRatio float64, app gowid curRatio := float64(prog.cur) / float64(prog.max) - if !prog.Complete() { - if prevRatio < curRatio { - loadProgress.SetTarget(app, int(prog.max)) - loadProgress.SetProgress(app, int(prog.cur)) - } + if prevRatio < curRatio { + loadProgress.SetTarget(app, int(prog.max)) + loadProgress.SetProgress(app, int(prog.cur)) } + return math.Max(prevRatio, curRatio) }