+// probeStop returns why a probe run must stop: a failed call, or a quota window
+// whose used percent grew by more than probeQuotaLimit points since the first
+// call. It returns "" when the run can go on.
+func probeStop(records []ModelUsageRecord) string {
+ for _, record := range records {
+ if record.Error != "" {
+ return "call failed: " + record.Error
+ }
+ }
+
+ if len(records) == 0 {
+ return ""
+ }
+
+ first := probeUsedPercents(records[0])
+ for name, used := range probeUsedPercents(records[len(records)-1]) {
+ start, found := first[name]
+ if found && used-start > probeQuotaLimit {
+ return fmt.Sprintf("%s grew from %g to %g", name, start, used)
+ }
+ }
+
+ return ""
+}
+
+// probeBuiltByModel tells if the node key needed a model call: a leaf whose
+// source is over nodeBytes, or a merge whose children do not fit in one node.
+func probeBuiltByModel(memory *Memory, key nodeKey) (bool, error) {
+ if key.level == 0 {
+ message, err := memory.log.Message(key.index)
+ if err != nil {
+ return false, err
+ }
+
+ _, free := freeLeaf(message)
+
+ return !free, nil
+ }
+
+ left, right, err := memory.children(key)
+ if err != nil {
+ return false, err
+ }
+
+ _, free := freeMerge(left, right)
+
+ return !free, nil
+}
+
+// probeCallStart returns the start of the call of record, or the zero time when
+// its date is not valid.
+func probeCallStart(record ModelUsageRecord) time.Time {
+ start, err := time.Parse(time.RFC3339Nano, record.Date)
+ if err != nil {
+ return time.Time{}
+ }
+
+ return start
+}
+
+// probeRatio returns a / b, or 0 when b is 0.
+func probeRatio(a, b int) float64 {
+ if b == 0 {
+ return 0
+ }
+
+ return float64(a) / float64(b)
+}
+
+// probeSizeText describes the sizes of the model lines.
+func probeSizeText(sizes []int) string {