blob: 27e03eecfa2b994cd52101c8c0a8cdb8710bc84c [file] [log] [blame]
Willy Tarreau75014fc2022-08-12 11:23:59 +02001/*
2 * post-mortem ring reader for haproxy
3 *
4 * Copyright (C) 2022 Willy Tarreau <w@1wt.eu>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27#include <sys/mman.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <stdlib.h>
33
34#include <haproxy/api.h>
35#include <haproxy/buf.h>
36#include <haproxy/ring.h>
37
38int force = 0; // force access to a different layout
Willy Tarreaucc51c9a2022-08-12 12:09:41 +020039int lfremap = 0; // remap LF in traces
40
Willy Tarreau75014fc2022-08-12 11:23:59 +020041
42/* display the message and exit with the code */
43__attribute__((noreturn)) void die(int code, const char *format, ...)
44{
45 va_list args;
46
47 if (format) {
48 va_start(args, format);
49 vfprintf(stderr, format, args);
50 va_end(args);
51 }
52 exit(code);
53}
54
55/* display the usage message and exit with the code */
56__attribute__((noreturn)) void usage(int code, const char *arg0)
57{
58 die(code,
59 "Usage: %s [options]* <file>\n"
60 "\n"
61 "options :\n"
62 " -f : force accessing a non-matching layout for 'ring struct'\n"
Willy Tarreaucc51c9a2022-08-12 12:09:41 +020063 " -l : replace LF in contents with CR VT\n"
Willy Tarreau75014fc2022-08-12 11:23:59 +020064 "\n"
65 "", arg0);
66}
67
68/* This function dumps all events from the ring whose pointer is in <p0> into
69 * the appctx's output buffer, and takes from <o0> the seek offset into the
70 * buffer's history (0 for oldest known event). It looks at <i0> for boolean
71 * options: bit0 means it must wait for new data or any key to be pressed. Bit1
72 * means it must seek directly to the end to wait for new contents. It returns
73 * 0 if the output buffer or events are missing is full and it needs to be
74 * called again, otherwise non-zero. It is meant to be used with
75 * cli_release_show_ring() to clean up.
76 */
77int dump_ring(struct ring *ring, size_t ofs, int flags)
78{
79 struct buffer buf;
80 uint64_t msg_len = 0;
81 size_t len, cnt;
Willy Tarreaucc51c9a2022-08-12 12:09:41 +020082 const char *blk1 = NULL, *blk2 = NULL, *p;
83 size_t len1 = 0, len2 = 0, bl;
Willy Tarreau75014fc2022-08-12 11:23:59 +020084
85 /* Explanation: the storage area in the writing process starts after
86 * the end of the structure. Since the whole area is mmapped(), we know
87 * it starts at 0 mod 4096, hence the buf->area pointer's 12 LSB point
88 * to the relative offset of the storage area. As there will always be
89 * users using the wrong version of the tool with a dump, we need to
90 * run a few checks first. After that we'll create our own buffer
91 * descriptor matching that area.
92 */
93 if ((((long)ring->buf.area) & 4095) != sizeof(*ring)) {
94 if (!force) {
95 fprintf(stderr, "FATAL: header in file is %ld bytes long vs %ld expected!\n",
96 (((long)ring->buf.area) & 4095),
97 (long)sizeof(*ring));
98 exit(1);
99 }
100 else {
101 fprintf(stderr, "WARNING: header in file is %ld bytes long vs %ld expected!\n",
102 (((long)ring->buf.area) & 4095),
103 (long)sizeof(*ring));
104 }
105 /* maybe we could emit a warning at least ? */
106 }
107
108 /* Now make our own buffer pointing to that area */
109 buf = b_make(((void *)ring + (((long)ring->buf.area) & 4095)),
110 ring->buf.size, ring->buf.head, ring->buf.data);
111
112 /* explanation for the initialization below: it would be better to do
113 * this in the parsing function but this would occasionally result in
114 * dropped events because we'd take a reference on the oldest message
115 * and keep it while being scheduled. Thus instead let's take it the
116 * first time we enter here so that we have a chance to pass many
117 * existing messages before grabbing a reference to a location. This
118 * value cannot be produced after initialization.
119 */
120 if (unlikely(ofs == ~0)) {
121 ofs = 0;
122
123 /* going to the end means looking at tail-1 */
124 if (flags & RING_WF_SEEK_NEW)
125 ofs += b_data(&buf) - 1;
126
127 //HA_ATOMIC_INC(b_peek(&buf, ofs));
128 ofs += ring->ofs;
129 }
130
131 while (1) {
132 //HA_RWLOCK_RDLOCK(LOGSRV_LOCK, &ring->lock);
133
134 /* we were already there, adjust the offset to be relative to
135 * the buffer's head and remove us from the counter.
136 */
137 ofs -= ring->ofs;
138 if (ofs >= buf.size) {
139 fprintf(stderr, "FATAL error at %d\n", __LINE__);
140 return 1;
141 }
142 //HA_ATOMIC_DEC(b_peek(&buf, ofs));
143
144 /* in this loop, ofs always points to the counter byte that precedes
145 * the message so that we can take our reference there if we have to
146 * stop before the end.
147 */
148 while (ofs + 1 < b_data(&buf)) {
149 cnt = 1;
150 len = b_peek_varint(&buf, ofs + cnt, &msg_len);
151 if (!len)
152 break;
153 cnt += len;
154
155 if (msg_len + ofs + cnt + 1 > buf.data) {
156 fprintf(stderr, "FATAL error at %d\n", __LINE__);
157 return 1;
158 }
159
160 len = b_getblk_nc(&buf, &blk1, &len1, &blk2, &len2, ofs + cnt, msg_len);
Willy Tarreaucc51c9a2022-08-12 12:09:41 +0200161 if (!lfremap) {
162 if (len > 0 && len1)
163 fwrite(blk1, len1, 1, stdout);
164 if (len > 1 && len2)
165 fwrite(blk2, len2, 1, stdout);
166 } else {
167 while (len > 0) {
168 for (; len1; p++) {
169 p = memchr(blk1, '\n', len1);
170 if (!p || p > blk1) {
171 bl = p ? p - blk1 : len1;
172 fwrite(blk1, bl, 1, stdout);
173 blk1 += bl;
174 len1 -= bl;
175 }
176
177 if (p) {
178 putchar('\r');
179 putchar('\v');
180 blk1++;
181 len1--;
182 }
183 }
184 len--;
185 blk1 = blk2;
186 len1 = len2;
187 }
188 }
189
190 putchar('\n');
Willy Tarreau75014fc2022-08-12 11:23:59 +0200191
192 ofs += cnt + msg_len;
193 }
194
195 //HA_ATOMIC_INC(b_peek(&buf, ofs));
196 ofs += ring->ofs;
197 //HA_RWLOCK_RDUNLOCK(LOGSRV_LOCK, &ring->lock);
198
199 if (!(flags & RING_WF_WAIT_MODE))
200 break;
201
202 /* pause 10ms before checking for new stuff */
203 usleep(10000);
204 }
205 return 0;
206}
207
208int main(int argc, char **argv)
209{
210 struct ring *ring;
211 struct stat statbuf;
212 const char *arg0;
213 int fd;
214
215 arg0 = argv[0];
216 while (argc > 1 && argv[1][0] == '-') {
217 argc--; argv++;
218 if (strcmp(argv[0], "-f") == 0)
219 force = 1;
Willy Tarreaucc51c9a2022-08-12 12:09:41 +0200220 else if (strcmp(argv[0], "-l") == 0)
221 lfremap = 1;
Willy Tarreau75014fc2022-08-12 11:23:59 +0200222 else if (strcmp(argv[0], "--") == 0)
223 break;
224 else
225 usage(1, arg0);
226 }
227
228 if (argc < 2)
229 usage(1, arg0);
230
231 fd = open(argv[1], O_RDONLY);
232 if (fd < 0) {
233 perror("open()");
234 return 1;
235 }
236
237 if (fstat(fd, &statbuf) < 0) {
238 perror("fstat()");
239 return 1;
240 }
241
242 ring = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
243 close(fd);
244
245 if (ring == MAP_FAILED) {
246 perror("mmap()");
247 return 1;
248 }
249
250 return dump_ring(ring, ~0, 0);
251}
252
253
254/*
255 * Local variables:
256 * c-indent-level: 8
257 * c-basic-offset: 8
258 * End:
259 */