| 1 | /* |
| 2 | * blktrace output analysis: generate a timeline & gather statistics |
| 3 | * |
| 4 | * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 19 | * |
| 20 | */ |
| 21 | #include <stdio.h> |
| 22 | #include <string.h> |
| 23 | #include <time.h> |
| 24 | |
| 25 | #include "blktrace.h" |
| 26 | #include "rbtree.h" |
| 27 | #include "list.h" |
| 28 | |
| 29 | /* |
| 30 | * 0 == 1 blk |
| 31 | * 1 == 2 blks |
| 32 | * ... |
| 33 | * 1022 == 1023 blks |
| 34 | * 1023 == 1024 blks |
| 35 | * 1024 == > 1024 blks |
| 36 | */ |
| 37 | #define N_HIST_BKTS 1025 |
| 38 | |
| 39 | #define BIT_TIME(t) ((double)SECONDS(t) + ((double)NANO_SECONDS(t) / 1.0e9)) |
| 40 | |
| 41 | #define BIT_START(iop) ((iop)->t.sector) |
| 42 | #define BIT_END(iop) ((iop)->t.sector + ((iop)->t.bytes >> 9)) |
| 43 | #define IOP_READ(iop) ((iop)->t.action & BLK_TC_ACT(BLK_TC_READ)) |
| 44 | #define IOP_RW(iop) (IOP_READ(iop) ? 1 : 0) |
| 45 | |
| 46 | #define TO_SEC(nanosec) ((double)(nanosec) / 1.0e9) |
| 47 | #define TO_MSEC(nanosec) (1000.0 * TO_SEC(nanosec)) |
| 48 | |
| 49 | enum iop_type { |
| 50 | IOP_Q = 0, |
| 51 | IOP_X = 1, |
| 52 | IOP_A = 2, |
| 53 | IOP_G = 3, |
| 54 | IOP_M = 4, |
| 55 | IOP_D = 5, |
| 56 | IOP_C = 6, |
| 57 | IOP_R = 7, |
| 58 | IOP_I = 8, |
| 59 | IOP_S = 9 |
| 60 | }; |
| 61 | #define N_IOP_TYPES (IOP_S + 1) |
| 62 | |
| 63 | struct mode { |
| 64 | int most_seeks, nmds; |
| 65 | long long *modes; |
| 66 | }; |
| 67 | |
| 68 | struct io; |
| 69 | struct io_list { |
| 70 | struct list_head head; |
| 71 | struct io *iop; |
| 72 | int cy_users; |
| 73 | }; |
| 74 | |
| 75 | struct avg_info { |
| 76 | __u64 min, max, total; |
| 77 | double avg; |
| 78 | int n; |
| 79 | }; |
| 80 | |
| 81 | struct avgs_info { |
| 82 | struct avg_info q2q_dm; |
| 83 | struct avg_info q2a_dm; |
| 84 | struct avg_info q2c_dm; |
| 85 | |
| 86 | struct avg_info q2q; |
| 87 | struct avg_info q2a; |
| 88 | struct avg_info q2g; |
| 89 | struct avg_info s2g; |
| 90 | struct avg_info g2i; |
| 91 | struct avg_info q2m; |
| 92 | struct avg_info i2d; |
| 93 | struct avg_info m2d; |
| 94 | struct avg_info d2c; |
| 95 | struct avg_info q2c; |
| 96 | |
| 97 | struct avg_info blks; /* Blocks transferred */ |
| 98 | }; |
| 99 | |
| 100 | struct range_info { |
| 101 | struct list_head head; /* on: qranges OR cranges */ |
| 102 | __u64 start, end; |
| 103 | }; |
| 104 | |
| 105 | struct region_info { |
| 106 | struct list_head qranges; |
| 107 | struct list_head cranges; |
| 108 | }; |
| 109 | |
| 110 | struct p_info { |
| 111 | struct region_info regions; |
| 112 | struct avgs_info avgs; |
| 113 | __u64 last_q; |
| 114 | __u32 pid; |
| 115 | char *name; |
| 116 | }; |
| 117 | |
| 118 | struct stats { |
| 119 | __u64 rqm[2], ios[2], sec[2], wait, svctm; |
| 120 | double last_qu_change, last_dev_change, tot_qusz, idle_time; |
| 121 | int cur_qusz, cur_dev; |
| 122 | }; |
| 123 | |
| 124 | struct stats_t { |
| 125 | double n; |
| 126 | double rqm_s[2], ios_s[2], sec_s[2]; |
| 127 | double avgrq_sz, avgqu_sz, await, svctm, p_util; |
| 128 | }; |
| 129 | |
| 130 | struct d_info { |
| 131 | struct list_head all_head, hash_head; |
| 132 | void *heads; |
| 133 | struct region_info regions; |
| 134 | char *devmap; |
| 135 | void *q2q_handle, *seek_handle, *bno_dump_handle, *up_hist_handle; |
| 136 | void *q2d_priv, *aqd_handle, *rstat_handle; |
| 137 | void *q2d_plat_handle, *q2c_plat_handle, *d2c_plat_handle; |
| 138 | FILE *q2d_ofp, *d2c_ofp, *q2c_ofp, *pit_fp; |
| 139 | struct avgs_info avgs; |
| 140 | struct stats stats, all_stats; |
| 141 | __u64 last_q, n_qs, n_ds; |
| 142 | __u64 n_act_q, t_act_q; /* # currently active when Q comes in */ |
| 143 | __u32 device; |
| 144 | |
| 145 | int pre_culling; |
| 146 | int is_plugged, nplugs, nplugs_t; |
| 147 | __u64 nios_up, nios_upt; |
| 148 | double start_time, last_plug, plugged_time, end_time; |
| 149 | }; |
| 150 | |
| 151 | struct io { |
| 152 | struct rb_node rb_node; |
| 153 | struct list_head f_head, a_head; |
| 154 | struct d_info *dip; |
| 155 | struct p_info *pip; |
| 156 | void *pdu; |
| 157 | __u64 bytes_left, g_time, i_time, m_time, d_time, c_time, d_sec, c_sec; |
| 158 | __u64 s_time; |
| 159 | __u32 d_nsec, c_nsec; |
| 160 | |
| 161 | struct blk_io_trace t; |
| 162 | |
| 163 | int linked; |
| 164 | enum iop_type type; |
| 165 | }; |
| 166 | |
| 167 | /* bt_timeline.c */ |
| 168 | |
| 169 | extern char bt_timeline_version[], *devices, *exes, *input_name, *output_name; |
| 170 | extern char *seek_name, *iostat_name, *d2c_name, *q2c_name, *per_io_name; |
| 171 | extern char *bno_dump_name, *unplug_hist_name, *sps_name, *aqd_name, *q2d_name; |
| 172 | extern char *per_io_trees; |
| 173 | extern double range_delta, plat_freq, last_t_seen; |
| 174 | extern FILE *rngs_ofp, *avgs_ofp, *xavgs_ofp, *iostat_ofp, *per_io_ofp; |
| 175 | extern FILE *msgs_ofp; |
| 176 | extern int verbose, done, time_bounded, output_all_data, seek_absolute; |
| 177 | extern int easy_parse_avgs, ignore_remaps; |
| 178 | extern unsigned int n_devs; |
| 179 | extern unsigned long n_traces; |
| 180 | extern struct list_head all_devs, all_procs; |
| 181 | extern struct avgs_info all_avgs; |
| 182 | extern __u64 last_q; |
| 183 | extern struct region_info all_regions; |
| 184 | extern struct list_head all_ios, free_ios; |
| 185 | extern __u64 iostat_interval, iostat_last_stamp; |
| 186 | extern time_t genesis, last_vtrace; |
| 187 | extern double t_astart, t_aend; |
| 188 | extern __u64 q_histo[N_HIST_BKTS], d_histo[N_HIST_BKTS]; |
| 189 | |
| 190 | /* args.c */ |
| 191 | void handle_args(int argc, char *argv[]); |
| 192 | void clean_args(); |
| 193 | |
| 194 | /* aqd.c */ |
| 195 | void *aqd_alloc(char *str); |
| 196 | void aqd_free(void *info); |
| 197 | void aqd_clean(void); |
| 198 | void aqd_issue(void *info, double ts); |
| 199 | void aqd_complete(void *info, double ts); |
| 200 | |
| 201 | /* devmap.c */ |
| 202 | int dev_map_read(char *fname); |
| 203 | char *dev_map_find(__u32 device); |
| 204 | void dev_map_exit(void); |
| 205 | |
| 206 | /* devs.c */ |
| 207 | void init_dev_heads(void); |
| 208 | struct d_info *dip_alloc(__u32 device, struct io *iop); |
| 209 | void iop_rem_dip(struct io *iop); |
| 210 | struct d_info *__dip_find(__u32 device); |
| 211 | void dip_foreach_list(struct io *iop, enum iop_type type, struct list_head *hd); |
| 212 | void dip_foreach(struct io *iop, enum iop_type type, |
| 213 | void (*fnc)(struct io *iop, struct io *this), int rm_after); |
| 214 | struct io *dip_find_sec(struct d_info *dip, enum iop_type type, __u64 sec); |
| 215 | void dip_foreach_out(void (*func)(struct d_info *, void *), void *arg); |
| 216 | void dip_plug(__u32 dev, double cur_time); |
| 217 | void dip_unplug(__u32 dev, double cur_time, __u64 nio_ups); |
| 218 | void dip_unplug_tm(__u32 dev, double cur_time, __u64 nio_ups); |
| 219 | void dip_exit(void); |
| 220 | void dip_cleanup(void); |
| 221 | |
| 222 | /* dip_rb.c */ |
| 223 | int rb_insert(struct rb_root *root, struct io *iop); |
| 224 | struct io *rb_find_sec(struct rb_root *root, __u64 sec); |
| 225 | void rb_foreach(struct rb_node *n, struct io *iop, |
| 226 | void (*fnc)(struct io *iop, struct io *this), |
| 227 | struct list_head *head); |
| 228 | |
| 229 | /* iostat.c */ |
| 230 | void iostat_init(void); |
| 231 | void iostat_getrq(struct io *iop); |
| 232 | void iostat_merge(struct io *iop); |
| 233 | void iostat_issue(struct io *iop); |
| 234 | void iostat_complete(struct io *d_iop, struct io *c_iop); |
| 235 | void iostat_check_time(__u64 stamp); |
| 236 | void iostat_dump_stats(__u64 stamp, int all); |
| 237 | |
| 238 | /* latency.c */ |
| 239 | void latency_alloc(struct d_info *dip); |
| 240 | void latency_clean(void); |
| 241 | void latency_q2d(struct d_info *dip, __u64 tstamp, __u64 latency); |
| 242 | void latency_d2c(struct d_info *dip, __u64 tstamp, __u64 latency); |
| 243 | void latency_q2c(struct d_info *dip, __u64 tstamp, __u64 latency); |
| 244 | |
| 245 | /* misc.c */ |
| 246 | void add_file(FILE *fp, char *oname); |
| 247 | void add_buf(void *buf); |
| 248 | char *make_dev_hdr(char *pad, size_t len, struct d_info *dip, int add_parens); |
| 249 | FILE *my_fopen(const char *path, const char *mode); |
| 250 | int my_open(const char *path, int flags); |
| 251 | void dbg_ping(void); |
| 252 | void clean_allocs(void); |
| 253 | |
| 254 | /* mmap.c */ |
| 255 | void setup_ifile(char *fname); |
| 256 | void cleanup_ifile(void); |
| 257 | int next_trace(struct blk_io_trace *t, void **pdu); |
| 258 | double pct_done(void); |
| 259 | |
| 260 | /* output.c */ |
| 261 | int output_avgs(FILE *ofp); |
| 262 | int output_ranges(FILE *ofp); |
| 263 | |
| 264 | /* proc.c */ |
| 265 | void process_alloc(__u32 pid, char *name); |
| 266 | struct p_info *find_process(__u32 pid, char *name); |
| 267 | void pip_update_q(struct io *iop); |
| 268 | void pip_foreach_out(void (*f)(struct p_info *, void *), void *arg); |
| 269 | void pip_exit(void); |
| 270 | |
| 271 | /* bno_dump.c */ |
| 272 | void *bno_dump_alloc(__u32 device); |
| 273 | void bno_dump_free(void *param); |
| 274 | void bno_dump_add(void *handle, struct io *iop); |
| 275 | void bno_dump_clean(void); |
| 276 | |
| 277 | /* plat.c */ |
| 278 | void *plat_alloc(char *str); |
| 279 | void plat_free(void *info); |
| 280 | void plat_clean(void); |
| 281 | void plat_x2c(void *info, __u64 ts, __u64 latency); |
| 282 | |
| 283 | /* q2d.c */ |
| 284 | void q2d_histo_add(void *priv, __u64 q2d); |
| 285 | void *q2d_alloc(void); |
| 286 | void q2d_free(void *priv); |
| 287 | void q2d_display_header(FILE *fp); |
| 288 | void q2d_display_dashes(FILE *fp); |
| 289 | void q2d_display(FILE *fp, void *priv); |
| 290 | int q2d_ok(void *priv); |
| 291 | void q2d_acc(void *a1, void *a2); |
| 292 | |
| 293 | /* rstats.c */ |
| 294 | void *rstat_alloc(char *bn); |
| 295 | void rstat_free(void *ptr); |
| 296 | void rstat_add(void *ptr, double cur, unsigned long long nblks); |
| 297 | int rstat_init(void); |
| 298 | void rstat_exit(void); |
| 299 | |
| 300 | /* seek.c */ |
| 301 | void *seeki_alloc(char *str); |
| 302 | void seeki_free(void *param); |
| 303 | void seek_clean(void); |
| 304 | void seeki_add(void *handle, struct io *iop); |
| 305 | double seeki_mean(void *handle); |
| 306 | long long seeki_nseeks(void *handle); |
| 307 | long long seeki_median(void *handle); |
| 308 | int seeki_mode(void *handle, struct mode *mp); |
| 309 | |
| 310 | /* trace.c */ |
| 311 | void add_trace(struct io *iop); |
| 312 | |
| 313 | /* trace_complete.c */ |
| 314 | void trace_complete(struct io *c_iop); |
| 315 | |
| 316 | /* trace_im.c */ |
| 317 | void run_im(struct io *im_iop, struct io *d_iop, struct io *c_iop); |
| 318 | void run_unim(struct io *im_iop, struct io *d_iop, struct io *c_iop); |
| 319 | int ready_im(struct io *im_iop, struct io *c_iop); |
| 320 | void trace_insert(struct io *i_iop); |
| 321 | void trace_merge(struct io *m_iop); |
| 322 | void trace_getrq(struct io *g_iop); |
| 323 | void trace_sleeprq(struct io *s_iop); |
| 324 | |
| 325 | /* trace_issue.c */ |
| 326 | void run_issue(struct io *d_iop, struct io *u_iop, struct io *c_iop); |
| 327 | void run_unissue(struct io *d_iop, struct io *u_iop, struct io *c_iop); |
| 328 | int ready_issue(struct io *d_iop, struct io *c_iop); |
| 329 | void trace_issue(struct io *d_iop); |
| 330 | |
| 331 | /* trace_plug.c */ |
| 332 | void trace_plug(struct io *p_iop); |
| 333 | void trace_unplug_io(struct io *u_iop); |
| 334 | void trace_unplug_timer(struct io *u_iop); |
| 335 | |
| 336 | /* trace_queue.c */ |
| 337 | void run_queue(struct io *q_iop, struct io *u_iop, struct io *c_iop); |
| 338 | int ready_queue(struct io *q_iop, struct io *c_iop); |
| 339 | void trace_queue(struct io *q_iop); |
| 340 | |
| 341 | /* trace_remap.c */ |
| 342 | void run_remap(struct io *a_iop, struct io *u_iop, struct io *c_iop); |
| 343 | int ready_remap(struct io *a_iop, struct io *c_iop); |
| 344 | void trace_remap(struct io *a_iop); |
| 345 | |
| 346 | /* trace_requeue.c */ |
| 347 | void trace_requeue(struct io *r_iop); |
| 348 | |
| 349 | /* unplug_hist.c */ |
| 350 | void *unplug_hist_alloc(__u32 device); |
| 351 | void unplug_hist_free(void *arg); |
| 352 | void unplug_hist_add(struct io *u_iop); |
| 353 | |
| 354 | #include "inlines.h" |