Skip to content

Commit

Permalink
Various improvements to the progress bar
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
gcla committed Aug 15, 2021
1 parent 7b1a111 commit 97fc9f3
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 58 deletions.
77 changes: 40 additions & 37 deletions cmd/termshark/termshark.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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)
Expand Down
55 changes: 34 additions & 21 deletions ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand All @@ -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
}
}
}

Expand All @@ -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)
}
Expand All @@ -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
}
}
}

Expand All @@ -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)
}
Expand Down Expand Up @@ -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)
}

Expand Down

0 comments on commit 97fc9f3

Please sign in to comment.