• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.32
/src/hist_source.cc
1
/**
2
 * Copyright (c) 2007-2012, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#include "hist_source.hh"
31

32
#include "base/math_util.hh"
33
#include "config.h"
34
#include "fmt/chrono.h"
35
#include "hist_source_T.hh"
36
#include "textinput_curses.hh"
37

38
std::optional<vis_line_t>
39
hist_source2::row_for_time(timeval tv_bucket)
139✔
40
{
41
    int retval = 0;
139✔
42
    auto time_bucket = rounddown(to_us(tv_bucket), this->hs_time_slice);
139✔
43

44
    for (auto& bb : this->hs_blocks) {
143✔
45
        if (time_bucket < bb.bb_buckets[0].b_time) {
139✔
46
            break;
14✔
47
        }
48
        if (time_bucket > bb.bb_buckets[bb.bb_used].b_time) {
125✔
49
            retval += bb.bb_used + 1;
4✔
50
            continue;
4✔
51
        }
52

53
        for (unsigned int lpc = 0; lpc <= bb.bb_used; lpc++, retval++) {
127✔
54
            if (time_bucket <= bb.bb_buckets[lpc].b_time) {
127✔
55
                return vis_line_t(retval);
121✔
56
            }
57
        }
58
    }
59
    return vis_line_t(retval);
18✔
60
}
61

62
line_info
63
hist_source2::text_value_for_line(textview_curses& tc,
11✔
64
                                  int row,
65
                                  std::string& value_out,
66
                                  line_flags_t flags)
67
{
68
    auto& bucket = this->find_bucket(row);
11✔
69
    struct tm bucket_tm;
70

71
    if (this->hs_needs_flush) {
11✔
72
        this->end_of_row();
11✔
73
    }
74

75
    value_out.clear();
11✔
76
    auto secs = to_time_t(bucket.b_time);
11✔
77
    if (gmtime_r(&secs, &bucket_tm) != nullptr) {
11✔
78
        fmt::format_to(std::back_inserter(value_out),
11✔
79
                       FMT_STRING(" {:%a %b %d %H:%M:%S}  "),
44✔
80
                       bucket_tm);
81
    } else {
UNCOV
82
        log_error("no time?");
×
83
    }
84
    fmt::format_to(
11✔
85
        std::back_inserter(value_out),
86
        FMT_STRING(" {:8L} normal  {:8L} errors  {:8L} warnings  {:8L} marks"),
22✔
87
        rint(bucket.value_for(hist_type_t::HT_NORMAL).hv_value),
11✔
88
        rint(bucket.value_for(hist_type_t::HT_ERROR).hv_value),
11✔
89
        rint(bucket.value_for(hist_type_t::HT_WARNING).hv_value),
11✔
90
        rint(bucket.value_for(hist_type_t::HT_MARK).hv_value));
11✔
91

92
    return {};
22✔
93
}
94

95
void
96
hist_source2::text_attrs_for_line(textview_curses& tc,
11✔
97
                                  int row,
98
                                  string_attrs_t& value_out)
99
{
100
    auto& bucket = this->find_bucket(row);
11✔
101
    auto dim = tc.get_dimensions();
11✔
102
    auto width = dim.second;
11✔
103
    int left = 0;
11✔
104

105
    if (width > 0 && tc.get_show_scrollbar()) {
11✔
106
        width -= 1;
2✔
107
    }
108
    for (int lpc = 0; lpc < lnav::enums::to_underlying(hist_type_t::HT__MAX);
55✔
109
         lpc++)
110
    {
111
        this->hs_chart.chart_attrs_for_value(tc,
88✔
112
                                             left,
113
                                             width,
114
                                             (const hist_type_t) lpc,
44✔
115
                                             bucket.b_values[lpc].hv_value,
116
                                             value_out);
117
    }
118
    auto alt_row_index = row % 4;
11✔
119
    if (alt_row_index == 2 || alt_row_index == 3) {
11✔
120
        value_out.emplace_back(line_range{0, -1},
2✔
121
                               VC_ROLE.value(role_t::VCR_ALT_ROW));
4✔
122
    }
123
}
11✔
124

125
void
126
hist_source2::add_value(std::chrono::microseconds ts,
10,379✔
127
                        hist_type_t htype,
128
                        double value)
129
{
130
    require_ge(ts.count(), this->hs_last_ts.count());
10,379✔
131

132
    ts = rounddown(ts, this->hs_time_slice);
10,379✔
133
    if (ts != this->hs_last_ts) {
10,379✔
134
        this->end_of_row();
840✔
135

136
        this->hs_current_row += 1;
840✔
137
        this->hs_last_ts = ts;
840✔
138
    }
139

140
    auto& bucket = this->find_bucket(this->hs_current_row);
10,379✔
141
    bucket.b_time = ts;
10,379✔
142
    bucket.value_for(htype).hv_value += value;
10,379✔
143

144
    this->hs_needs_flush = true;
10,379✔
145
}
10,379✔
146

147
void
148
hist_source2::init()
5,077✔
149
{
150
    auto& vc = view_colors::singleton();
5,077✔
151

152
    this->hs_chart.with_show_state(stacked_bar_chart_base::show_all{})
5,077✔
153
        .with_attrs_for_ident(hist_type_t::HT_NORMAL,
5,077✔
154
                              vc.attrs_for_role(role_t::VCR_TEXT))
10,154✔
155
        .with_attrs_for_ident(hist_type_t::HT_WARNING,
5,077✔
156
                              vc.attrs_for_role(role_t::VCR_WARNING))
10,154✔
157
        .with_attrs_for_ident(hist_type_t::HT_ERROR,
5,077✔
158
                              vc.attrs_for_role(role_t::VCR_ERROR))
10,154✔
159
        .with_attrs_for_ident(hist_type_t::HT_MARK,
5,077✔
160
                              vc.attrs_for_role(role_t::VCR_COMMENT));
10,154✔
161
}
5,077✔
162

163
void
164
hist_source2::clear()
4,498✔
165
{
166
    this->hs_line_count = 0;
4,498✔
167
    this->hs_current_row = -1;
4,498✔
168
    this->hs_last_ts = std::chrono::microseconds::min();
4,498✔
169
    this->hs_blocks.clear();
4,498✔
170
    this->hs_chart.clear();
4,498✔
171
    if (this->tss_view != nullptr) {
4,498✔
172
        this->tss_view->get_bookmarks().clear();
3,407✔
173
    }
174
    this->init();
4,498✔
175
}
4,498✔
176

177
void
178
hist_source2::end_of_row()
851✔
179
{
180
    if (this->hs_current_row >= 0) {
851✔
181
        auto& last_bucket = this->find_bucket(this->hs_current_row);
287✔
182

183
        for (size_t lpc = 0;
1,435✔
184
             lpc < lnav::enums::to_underlying(hist_type_t::HT__MAX);
1,435✔
185
             lpc++)
186
        {
187
            auto& hv = last_bucket.b_values[lpc];
1,148✔
188
            this->hs_chart.add_value((const hist_type_t) lpc, hv.hv_value);
1,148✔
189

190
            if (hv.hv_value > 0.0) {
1,148✔
191
                const bookmark_type_t* bt = nullptr;
397✔
192
                switch ((hist_type_t) lpc) {
397✔
193
                    case hist_type_t::HT_WARNING: {
6✔
194
                        bt = &textview_curses::BM_WARNINGS;
6✔
195
                        break;
6✔
196
                    }
197
                    case hist_type_t::HT_ERROR: {
131✔
198
                        bt = &textview_curses::BM_ERRORS;
131✔
199
                        break;
131✔
200
                    }
201
                    case hist_type_t::HT_MARK: {
2✔
202
                        bt = &textview_curses::BM_META;
2✔
203
                        break;
2✔
204
                    }
205
                    default:
258✔
206
                        break;
258✔
207
                }
208
                if (bt != nullptr) {
397✔
209
                    auto& bm = this->tss_view->get_bookmarks();
139✔
210
                    bm[bt].insert_once(vis_line_t(this->hs_current_row));
139✔
211
                }
212
            }
213
        }
214
        this->hs_chart.next_row();
287✔
215
    }
216
}
851✔
217

218
std::optional<text_time_translator::row_info>
219
hist_source2::time_for_row(vis_line_t row)
579✔
220
{
221
    if (row < 0 || row > this->hs_line_count) {
579✔
UNCOV
222
        return std::nullopt;
×
223
    }
224

225
    const auto& bucket = this->find_bucket(row);
579✔
226

227
    return row_info{timeval{to_time_t(bucket.b_time), 0}, row};
579✔
228
}
229

230
hist_source2::bucket_t&
231
hist_source2::find_bucket(int64_t index)
11,267✔
232
{
233
    const auto block_index = index / BLOCK_SIZE;
11,267✔
234
    if (block_index >= (ssize_t) this->hs_blocks.size()) {
11,267✔
235
        this->hs_blocks.resize(block_index + 1);
564✔
236
    }
237
    auto& bb = this->hs_blocks[block_index];
11,267✔
238
    const unsigned int intra_block_index = index % BLOCK_SIZE;
11,267✔
239
    bb.bb_used = std::max(intra_block_index, bb.bb_used);
11,267✔
240
    this->hs_line_count = std::max(this->hs_line_count, index + 1);
11,267✔
241
    return bb.bb_buckets[intra_block_index];
11,267✔
242
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc