1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * ipath.c -- instanced pathname module 26 * 27 * this module provides a cache of fully instantized component paths, 28 * stored in a fairly compact format. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <stdio.h> 34 #include <string.h> 35 #include "alloc.h" 36 #include "out.h" 37 #include "lut.h" 38 #include "tree.h" 39 #include "ptree.h" 40 #include "itree.h" 41 #include "ipath.h" 42 #include "stats.h" 43 #include "eval.h" 44 #include "config.h" 45 46 static struct stats *Nipath; 47 static struct stats *Nbytes; 48 49 /* an ipath cache entry is an array of these, with s==NULL at the end */ 50 struct ipath { 51 const char *s; /* component name (in stable) */ 52 int i; /* instance number */ 53 }; 54 55 static struct lut *Ipaths; /* the ipath cache itself */ 56 57 /* 58 * ipath_init -- initialize the ipath module 59 */ 60 void 61 ipath_init(void) 62 { 63 Nipath = stats_new_counter("ievent.nipath", "ipath cache entries", 1); 64 Nbytes = stats_new_counter("ievent.nbytes", "total cache size", 1); 65 } 66 67 /* 68 * ipath_cmp -- compare two ipath entries 69 * 70 * since two ipaths containing the same components and instance 71 * numbers always point to the same cache entry, they are equal 72 * if their pointers are equal, so this function is not necessary 73 * to test if two ipaths are same. but when inserting a new ipath 74 * into the cache, we must use the same lut comparison logic as when 75 * we're searching for it, so this function must always match the 76 * itree_epnamecmp() function's logic (see below) for searching the lut. 77 */ 78 static int 79 ipath_cmp(struct ipath *ipp1, struct ipath *ipp2) 80 { 81 int i; 82 83 ASSERT(ipp1 != NULL); 84 ASSERT(ipp2 != NULL); 85 86 for (i = 0; ipp1[i].s != NULL && ipp2[i].s != NULL; i++) 87 if (ipp1[i].s != ipp2[i].s) 88 return (ipp2[i].s - ipp1[i].s); 89 else if (ipp1[i].i != ipp2[i].i) 90 return (ipp2[i].i - ipp1[i].i); 91 92 if (ipp1[i].s == NULL && ipp2[i].s == NULL) 93 return (0); 94 else if (ipp1[i].s == NULL) 95 return (1); 96 else 97 return (-1); 98 } 99 100 /* 101 * ipath_epnamecmp -- compare an ipath with a struct node *epname list 102 * 103 * this function is used when searching the cache, allowing us to search 104 * a lut full of ipaths by looking directly at a struct node *epname 105 * (without having to convert it first). the comparison logic here must 106 * exactly match itree_cmp()'s logic (see above) so lut lookups use find 107 * the same node as lut inserts. 108 */ 109 static int 110 ipath_epnamecmp(struct ipath *ipp, struct node *np) 111 { 112 int i; 113 114 ASSERT(np != NULL); 115 ASSERT(ipp != NULL); 116 117 for (i = 0; ipp[i].s != NULL && np != NULL; i++, np = np->u.name.next) { 118 ASSERTinfo(np->t == T_NAME, ptree_nodetype2str(np->t)); 119 120 if (ipp[i].s != np->u.name.s) 121 return (np->u.name.s - ipp[i].s); 122 else { 123 int inum; 124 125 if (np->u.name.child != NULL && 126 np->u.name.child->t == T_NUM) 127 inum = (int)np->u.name.child->u.ull; 128 else 129 config_getcompname(np->u.name.cp, NULL, &inum); 130 131 if (ipp[i].i != inum) 132 return (inum - ipp[i].i); 133 } 134 } 135 136 if (ipp[i].s == NULL && np == NULL) 137 return (0); 138 else if (ipp[i].s == NULL) 139 return (1); 140 else 141 return (-1); 142 } 143 144 struct lut *Usednames; 145 146 void 147 ipath_dummy_lut(struct arrow *arrowp) 148 { 149 const struct ipath *ipp; 150 151 ipp = arrowp->head->myevent->ipp; 152 while (ipp->s != NULL) { 153 Usednames = lut_add(Usednames, (void *)ipp->s, 154 (void *)ipp->s, NULL); 155 ipp++; 156 } 157 ipp = arrowp->tail->myevent->ipp; 158 while (ipp->s != NULL) { 159 Usednames = lut_add(Usednames, (void *)ipp->s, 160 (void *)ipp->s, NULL); 161 ipp++; 162 } 163 } 164 165 struct ipath * 166 ipath_dummy(struct node *np, struct ipath *ipp) 167 { 168 struct ipath *ret; 169 170 ret = ipp; 171 while (ipp[1].s != NULL) 172 ipp++; 173 if (strcmp(ipp[0].s, np->u.name.last->u.name.s) == 0) 174 return (ret); 175 176 ret = MALLOC(sizeof (*ret) * 2); 177 ret[0].s = np->u.name.last->u.name.s; 178 ret[0].i = 0; 179 ret[1].s = NULL; 180 if ((ipp = lut_lookup(Ipaths, (void *)ret, 181 (lut_cmp)ipath_cmp)) != NULL) { 182 FREE(ret); 183 return (ipp); 184 } 185 Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, (lut_cmp)ipath_cmp); 186 stats_counter_bump(Nipath); 187 stats_counter_add(Nbytes, 2 * sizeof (struct ipath)); 188 return (ret); 189 } 190 191 /* 192 * ipath -- find instanced path in cache, or add it if necessary 193 */ 194 const struct ipath * 195 ipath(struct node *np) 196 { 197 struct ipath *ret; 198 int count; 199 struct node *namep; 200 int i; 201 202 if ((ret = lut_lookup(Ipaths, (void *)np, 203 (lut_cmp)ipath_epnamecmp)) != NULL) 204 return (ret); /* already in cache */ 205 206 /* 207 * not in cache, make new cache entry. 208 * start by counting the length of the name. 209 */ 210 count = 0; 211 namep = np; 212 while (namep != NULL) { 213 ASSERTinfo(namep->t == T_NAME, ptree_nodetype2str(namep->t)); 214 count++; 215 namep = namep->u.name.next; 216 } 217 218 ASSERT(count > 0); 219 220 /* allocate array for name and last NULL entry */ 221 ret = MALLOC(sizeof (*ret) * (count + 1)); 222 ret[count].s = NULL; 223 224 /* fill in ipath entry */ 225 namep = np; 226 i = 0; 227 while (namep != NULL) { 228 ASSERT(i < count); 229 ret[i].s = namep->u.name.s; 230 if (namep->u.name.child != NULL && 231 namep->u.name.child->t == T_NUM) 232 ret[i].i = (int)namep->u.name.child->u.ull; 233 else 234 config_getcompname(namep->u.name.cp, NULL, &ret[i].i); 235 i++; 236 namep = namep->u.name.next; 237 } 238 239 /* add it to the cache */ 240 Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, 241 (lut_cmp)ipath_cmp); 242 243 stats_counter_bump(Nipath); 244 stats_counter_add(Nbytes, (count + 1) * sizeof (struct ipath)); 245 246 return (ret); 247 } 248 249 /* 250 * ipath2str -- convert ename and ipath to class@path string 251 * 252 * if both ename and ipp are provided (non-NULL), the resulting string 253 * will be "class@path". otherwise, the string will just contain the 254 * event class name (e.g. "ereport.io.pci.device") or just the path 255 * name (e.g. "mothboard0/hostbridge0/pcibus1/pcidev0/pcifn1"), depending 256 * on which argument is non-NULL. 257 */ 258 char * 259 ipath2str(const char *ename, const struct ipath *ipp) 260 { 261 int i; 262 size_t len = 0; 263 char *ret; 264 char *cp; 265 266 /* count up length of class string */ 267 if (ename != NULL) 268 len += strlen(ename); 269 270 /* count up length of path string, including slash separators */ 271 if (ipp != NULL) { 272 for (i = 0; ipp[i].s != NULL; i++) { 273 /* add slash separator, but no leading slash */ 274 if (i != 0) 275 len++; 276 len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i); 277 } 278 } 279 280 if (ename != NULL && ipp != NULL) 281 len++; /* room for '@' */ 282 283 len++; /* room for final '\0' */ 284 285 cp = ret = MALLOC(len); 286 287 if (ename != NULL) { 288 /* construct class string */ 289 (void) strcpy(cp, ename); 290 cp += strlen(cp); 291 } 292 293 /* if doing both strings, put '@' between them */ 294 if (ename != NULL && ipp != NULL) 295 *cp++ = '@'; 296 297 if (ipp != NULL) { 298 /* construct path string */ 299 for (i = 0; ipp[i].s != NULL; i++) { 300 if (i != 0) 301 *cp++ = '/'; 302 (void) snprintf(cp, &ret[len] - cp, "%s%d", 303 ipp[i].s, ipp[i].i); 304 cp += strlen(cp); 305 } 306 } 307 308 *cp++ = '\0'; 309 310 return (ret); 311 } 312 313 /* 314 * ipath2strlen -- calculate the len of what ipath2str() would return 315 */ 316 size_t 317 ipath2strlen(const char *ename, const struct ipath *ipp) 318 { 319 int i; 320 size_t len = 0; 321 322 /* count up length of class string */ 323 if (ename != NULL) 324 len += strlen(ename); 325 326 /* count up length of path string, including slash separators */ 327 if (ipp != NULL) { 328 for (i = 0; ipp[i].s != NULL; i++) { 329 /* add slash separator, but no leading slash */ 330 if (i != 0) 331 len++; 332 len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i); 333 } 334 } 335 336 if (ename != NULL && ipp != NULL) 337 len++; /* room for '@' */ 338 339 return (len); 340 } 341 342 /* 343 * ipath_print -- print out an ename, ipath, or both with '@' between them 344 */ 345 void 346 ipath_print(int flags, const char *ename, const struct ipath *ipp) 347 { 348 if (ename != NULL) { 349 out(flags|O_NONL, ename); 350 if (ipp != NULL) 351 out(flags|O_NONL, "@"); 352 } 353 if (ipp != NULL) { 354 char *sep = ""; 355 356 while (ipp->s != NULL) { 357 out(flags|O_NONL, "%s%s%d", sep, ipp->s, ipp->i); 358 ipp++; 359 sep = "/"; 360 } 361 } 362 } 363 364 /*ARGSUSED*/ 365 static void 366 ipath_destructor(void *left, void *right, void *arg) 367 { 368 struct ipath *ipp = (struct ipath *)right; 369 370 FREE(ipp); 371 } 372 373 /* 374 * ipath_fini -- free the ipath cache 375 */ 376 void 377 ipath_fini(void) 378 { 379 lut_free(Ipaths, ipath_destructor, NULL); 380 Ipaths = NULL; 381 382 if (Nipath) { 383 stats_delete(Nipath); 384 Nipath = NULL; 385 } 386 387 if (Nbytes) { 388 stats_delete(Nbytes); 389 Nbytes = NULL; 390 } 391 } 392