1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include "P_Cache.h"
25 
26 #include "Show.h"
27 #include "I_Tasks.h"
28 
29 struct ShowCacheInternal : public ShowCont {
30   int vol_index = 0;
31   int seg_index = 0;
32   CacheKey show_cache_key;
33   CacheVC *cache_vc = nullptr;
34 
35   int showMain(int event, Event *e);
36   int showEvacuations(int event, Event *e);
37   int showVolEvacuations(int event, Event *e);
38   int showVolumes(int event, Event *e);
39   int showVolVolumes(int event, Event *e);
40   int showSegments(int event, Event *e);
41   int showSegSegment(int event, Event *e);
42 #ifdef CACHE_STAT_PAGES
43   int showConnections(int event, Event *e);
44   int showVolConnections(int event, Event *e);
45 #endif
46 
ShowCacheInternalShowCacheInternal47   ShowCacheInternal(Continuation *c, HTTPHdr *h) : ShowCont(c, h) { SET_HANDLER(&ShowCacheInternal::showMain); }
48 
~ShowCacheInternalShowCacheInternal49   ~ShowCacheInternal() override {}
50 };
51 extern ShowCacheInternal *theshowcacheInternal;
52 Action *register_ShowCacheInternal(Continuation *c, HTTPHdr *h);
53 
54 extern Vol **gvol;
55 
56 // Stat Pages
57 ShowCacheInternal *theshowcacheInternal = nullptr;
58 
59 #define STREQ_PREFIX(_x, _s) (!strncasecmp(_x, _s, sizeof(_s) - 1))
60 #define STREQ_LEN_PREFIX(_x, _l, _s) (path_len < sizeof(_s) && !strncasecmp(_x, _s, sizeof(_s) - 1))
61 
62 Action *
register_ShowCacheInternal(Continuation * c,HTTPHdr * h)63 register_ShowCacheInternal(Continuation *c, HTTPHdr *h)
64 {
65   theshowcacheInternal = new ShowCacheInternal(c, h);
66   URL *u               = h->url_get();
67 
68   int path_len;
69   const char *path = u->path_get(&path_len);
70 
71   if (!path) {
72   }
73 #ifdef CACHE_STAT_PAGES
74   else if (STREQ_LEN_PREFIX(path, path_len, "connections")) {
75     SET_CONTINUATION_HANDLER(theshowcacheInternal, &ShowCacheInternal::showConnections);
76   }
77 #endif
78   else if (STREQ_PREFIX(path, "evacuations")) {
79     SET_CONTINUATION_HANDLER(theshowcacheInternal, &ShowCacheInternal::showEvacuations);
80   } else if (STREQ_PREFIX(path, "volumes")) {
81     SET_CONTINUATION_HANDLER(theshowcacheInternal, &ShowCacheInternal::showVolumes);
82   }
83 
84   if (theshowcacheInternal->mutex->thread_holding) {
85     CONT_SCHED_LOCK_RETRY(theshowcacheInternal);
86   } else {
87     eventProcessor.schedule_imm(theshowcacheInternal, ET_TASK);
88   }
89   return &theshowcacheInternal->action;
90 }
91 
92 int
showMain(int event,Event * e)93 ShowCacheInternal::showMain(int event, Event *e)
94 {
95   CHECK_SHOW(begin("Cache"));
96 #ifdef CACHE_STAT_PAGES
97   CHECK_SHOW(show("<H3>Show <A HREF=\"./connections\">Connections</A></H3>\n"
98                   "<H3>Show <A HREF=\"./evacuations\">Evacuations</A></H3>\n"
99                   "<H3>Show <A HREF=\"./volumes\">Volumes</A></H3>\n"));
100 #else
101   CHECK_SHOW(show("<H3>Show <A HREF=\"./evacuations\">Evacuations</A></H3>\n"
102                   "<H3>Show <A HREF=\"./volumes\">Volumes</A></H3>\n"));
103 #endif
104   return complete(event, e);
105 }
106 
107 #ifdef CACHE_STAT_PAGES
108 int
showConnections(int event,Event * e)109 ShowCacheInternal::showConnections(int event, Event *e)
110 {
111   CHECK_SHOW(begin("Cache VConnections"));
112   CHECK_SHOW(show("<H3>Cache Connections</H3>\n"
113                   "<table border=1><tr>"
114                   "<th>Operation</th>"
115                   "<th>Volume</th>"
116                   "<th>URL/Hash</th>"
117                   "<th>Bytes Done</th>"
118                   "<th>Total Bytes</th>"
119                   "<th>Bytes Todo</th>"
120                   "</tr>\n"));
121 
122   SET_HANDLER(&ShowCacheInternal::showVolConnections);
123   CONT_SCHED_LOCK_RETRY_RET(this);
124 }
125 
126 int
showVolConnections(int event,Event * e)127 ShowCacheInternal::showVolConnections(int event, Event *e)
128 {
129   CACHE_TRY_LOCK(lock, gvol[vol_index]->mutex, mutex->thread_holding);
130   if (!lock) {
131     CONT_SCHED_LOCK_RETRY_RET(this);
132   }
133   for (CacheVC *vc = (CacheVC *)gvol[vol_index]->stat_cache_vcs.head; vc; vc = vc->stat_link.next) {
134     char nbytes[60], todo[60], url[81092];
135     int ib = 0, xd = 0;
136     URL uu;
137 
138     SCOPED_MUTEX_LOCK(lock2, vc->mutex, mutex->thread_holding);
139     // if vc is closed ignore - Ramki 08/30/2000
140     if (vc->closed == 1)
141       continue;
142     sprintf(nbytes, "%d", vc->vio.nbytes);
143     sprintf(todo, "%d", vc->vio.ntodo());
144 
145     if (vc->f.frag_type == CACHE_FRAG_TYPE_HTTP && vc->request.valid()) {
146       URL *u = vc->request.url_get(&uu);
147       u->print(url, 8000, &ib, &xd);
148       url[ib] = 0;
149     } else if (vc->alternate.valid()) {
150       URL *u = vc->alternate.request_url_get(&uu);
151       u->print(url, 8000, &ib, &xd);
152       url[ib] = 0;
153     } else
154       vc->key.string(url);
155     CHECK_SHOW(show("<tr>"
156                     "<td>%s</td>" // operation
157                     "<td>%s</td>" // Vol
158                     "<td>%s</td>" // URL/Hash
159                     "<td>%d</td>"
160                     "<td>%s</td>"
161                     "<td>%s</td>"
162                     "</tr>\n",
163                     ((vc->vio.op == VIO::READ) ? "Read" : "Write"), vc->vol->hash_id, url, vc->vio.ndone,
164                     vc->vio.nbytes == INT64_MAX ? "all" : nbytes, vc->vio.nbytes == INT64_MAX ? "all" : todo));
165   }
166   vol_index++;
167   if (vol_index < gnvol)
168     CONT_SCHED_LOCK_RETRY(this);
169   else {
170     CHECK_SHOW(show("</table>\n"));
171     return complete(event, e);
172   }
173   return EVENT_CONT;
174 }
175 
176 #endif
177 
178 int
showEvacuations(int event,Event * e)179 ShowCacheInternal::showEvacuations(int event, Event *e)
180 {
181   CHECK_SHOW(begin("Cache Pending Evacuations"));
182   CHECK_SHOW(show("<H3>Cache Evacuations</H3>\n"
183                   "<table border=1><tr>"
184                   "<th>Offset</th>"
185                   "<th>Estimated Size</th>"
186                   "<th>Reader Count</th>"
187                   "<th>Done</th>"
188                   "</tr>\n"));
189 
190   SET_HANDLER(&ShowCacheInternal::showVolEvacuations);
191   CONT_SCHED_LOCK_RETRY_RET(this);
192 }
193 
194 int
showVolEvacuations(int event,Event * e)195 ShowCacheInternal::showVolEvacuations(int event, Event *e)
196 {
197   Vol *p = gvol[vol_index];
198   CACHE_TRY_LOCK(lock, p->mutex, mutex->thread_holding);
199   if (!lock.is_locked()) {
200     CONT_SCHED_LOCK_RETRY_RET(this);
201   }
202 
203   EvacuationBlock *b;
204   int last = (p->len - (p->start - p->skip)) / EVACUATION_BUCKET_SIZE;
205   for (int i = 0; i < last; i++) {
206     for (b = p->evacuate[i].head; b; b = b->link.next) {
207       char offset[60];
208       sprintf(offset, "%" PRIu64 "", static_cast<uint64_t>(p->vol_offset(&b->dir)));
209       CHECK_SHOW(show("<tr>"
210                       "<td>%s</td>" // offset
211                       "<td>%d</td>" // estimated size
212                       "<td>%d</td>" // reader count
213                       "<td>%s</td>" // done
214                       "</tr>\n",
215                       offset, (int)dir_approx_size(&b->dir), b->readers, b->f.done ? "yes" : "no"));
216     }
217   }
218   vol_index++;
219   if (vol_index < gnvol) {
220     CONT_SCHED_LOCK_RETRY(this);
221   } else {
222     CHECK_SHOW(show("</table>\n"));
223     return complete(event, e);
224   }
225   return EVENT_CONT;
226 }
227 
228 int
showVolumes(int event,Event * e)229 ShowCacheInternal::showVolumes(int event, Event *e)
230 {
231   CHECK_SHOW(begin("Cache Volumes"));
232   CHECK_SHOW(show("<H3>Cache Volumes</H3>\n"
233                   "<table border=1><tr>"
234                   "<th>ID</th>"
235                   "<th>Blocks</th>"
236                   "<th>Directory Entries</th>"
237                   "<th>Write Position</th>"
238                   "<th>Write Agg Todo</th>"
239                   "<th>Write Agg Todo Size</th>"
240                   "<th>Write Agg Done</th>"
241                   "<th>Phase</th>"
242                   "<th>Create Time</th>"
243                   "<th>Sync Serial</th>"
244                   "<th>Write Serial</th>"
245                   "</tr>\n"));
246 
247   SET_HANDLER(&ShowCacheInternal::showVolVolumes);
248   CONT_SCHED_LOCK_RETRY_RET(this);
249 }
250 
251 int
showVolVolumes(int event,Event * e)252 ShowCacheInternal::showVolVolumes(int event, Event *e)
253 {
254   Vol *p = gvol[vol_index];
255   CACHE_TRY_LOCK(lock, p->mutex, mutex->thread_holding);
256   if (!lock.is_locked()) {
257     CONT_SCHED_LOCK_RETRY_RET(this);
258   }
259 
260   char ctime[256];
261   ink_ctime_r(&p->header->create_time, ctime);
262   ctime[strlen(ctime) - 1] = 0;
263   int agg_todo             = 0;
264   int agg_done             = p->agg_buf_pos;
265   CacheVC *c               = nullptr;
266   for (c = p->agg.head; c; c = (CacheVC *)c->link.next) {
267     agg_todo++;
268   }
269   CHECK_SHOW(show("<tr>"
270                   "<td>%s</td>"          // ID
271                   "<td>%" PRId64 "</td>" // blocks
272                   "<td>%" PRId64 "</td>" // directory entries
273                   "<td>%" PRId64 "</td>" // write position
274                   "<td>%d</td>"          // write agg to do
275                   "<td>%d</td>"          // write agg to do size
276                   "<td>%d</td>"          // write agg done
277                   "<td>%d</td>"          // phase
278                   "<td>%s</td>"          // create time
279                   "<td>%u</td>"          // sync serial
280                   "<td>%u</td>"          // write serial
281                   "</tr>\n",
282                   p->hash_text.get(), (uint64_t)((p->len - (p->start - p->skip)) / CACHE_BLOCK_SIZE),
283                   (uint64_t)(p->buckets * DIR_DEPTH * p->segments),
284                   (uint64_t)((p->header->write_pos - p->start) / CACHE_BLOCK_SIZE), agg_todo, p->agg_todo_size, agg_done,
285                   p->header->phase, ctime, p->header->sync_serial, p->header->write_serial));
286   CHECK_SHOW(show("</table>\n"));
287   SET_HANDLER(&ShowCacheInternal::showSegments);
288   return showSegments(event, e);
289 }
290 
291 int
showSegments(int event,Event * e)292 ShowCacheInternal::showSegments(int event, Event *e)
293 {
294   CHECK_SHOW(show("<H3>Cache Volume Segments</H3>\n"
295                   "<table border=1><tr>"
296                   "<th>Free</th>"
297                   "<th>Used</th>"
298                   "<th>Empty</th>"
299                   "<th>Valid</th>"
300                   "<th>Agg Valid</th>"
301                   "<th>Avg Size</th>"
302                   "</tr>\n"));
303 
304   SET_HANDLER(&ShowCacheInternal::showSegSegment);
305   seg_index = 0;
306   CONT_SCHED_LOCK_RETRY_RET(this);
307 }
308 
309 int
showSegSegment(int event,Event * e)310 ShowCacheInternal::showSegSegment(int event, Event *e)
311 {
312   Vol *p = gvol[vol_index];
313   CACHE_TRY_LOCK(lock, p->mutex, mutex->thread_holding);
314   if (!lock.is_locked()) {
315     CONT_SCHED_LOCK_RETRY_RET(this);
316   }
317   int free = 0, used = 0, empty = 0, valid = 0, agg_valid = 0, avg_size = 0;
318   dir_segment_accounted(seg_index, p, 0, &free, &used, &empty, &valid, &agg_valid, &avg_size);
319   CHECK_SHOW(show("<tr>"
320                   "<td>%d</td>"
321                   "<td>%d</td>"
322                   "<td>%d</td>"
323                   "<td>%d</td>"
324                   "<td>%d</td>"
325                   "<td>%d</td>"
326                   "</tr>\n",
327                   free, used, empty, valid, agg_valid, avg_size));
328   seg_index++;
329   if (seg_index < p->segments) {
330     CONT_SCHED_LOCK_RETRY(this);
331   } else {
332     CHECK_SHOW(show("</table>\n"));
333     seg_index = 0;
334     vol_index++;
335     if (vol_index < gnvol) {
336       CONT_SCHED_LOCK_RETRY(this);
337     } else {
338       return complete(event, e);
339     }
340   }
341   return EVENT_CONT;
342 }
343