VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTTraceLogTool.cpp@ 104921

Last change on this file since 104921 was 104921, checked in by vboxsync, 8 months ago

Runtime/tools/RTTraceLogTool,Devices/VBoxTraceLogDecoders.cpp: Allow attaching a state for a decoder to enable analysis spanning multiple events, bugref:10701 [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.5 KB
Line 
1/* $Id: RTTraceLogTool.cpp 104921 2024-06-14 12:04:58Z vboxsync $ */
2/** @file
3 * IPRT - Utility for reading/receiving and dissecting trace logs.
4 */
5
6/*
7 * Copyright (C) 2018-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/tracelog.h>
42#include <iprt/tracelog-decoder-plugin.h>
43
44#include <iprt/assert.h>
45#include <iprt/errcore.h>
46#include <iprt/getopt.h>
47#include <iprt/initterm.h>
48#include <iprt/ldr.h>
49#include <iprt/message.h>
50#include <iprt/mem.h>
51#include <iprt/path.h>
52#include <iprt/stream.h>
53#include <iprt/string.h>
54#include <iprt/tcp.h>
55
56
57typedef struct RTTRACELOGDECODER
58{
59 /** The tracelog decoder registration structure. */
60 PCRTTRACELOGDECODERREG pReg;
61 /** The helper structure for this decoder. */
62 RTTRACELOGDECODERHLP Hlp;
63 /** The decoder state if created. */
64 void *pvDecoderState;
65 /** The free callback of any attached decoder state. */
66 PFNTRACELOGDECODERSTATEFREE pfnDecoderStateFree;
67} RTTRACELOGDECODER;
68typedef RTTRACELOGDECODER *PRTTRACELOGDECODER;
69typedef const RTTRACELOGDECODER *PCRTTRACELOGDECODER;
70
71
72/**
73 * Loaded tracelog decoders.
74 */
75typedef struct RTTRACELOGDECODERSTATE
76{
77 /** Pointer to the array of registered decoders. */
78 PRTTRACELOGDECODER paDecoders;
79 /** Number of entries in the decoder array. */
80 uint32_t cDecoders;
81 /** Allocation size of the decoder array. */
82 uint32_t cDecodersAlloc;
83} RTTRACELOGDECODERSTATE;
84typedef RTTRACELOGDECODERSTATE *PRTTRACELOGDECODERSTATE;
85
86
87/**
88 * The tracelog tool TCP server/client state.
89 */
90typedef struct RTTRACELOGTOOLTCP
91{
92 /** Flag whether this is a server. */
93 bool fIsServer;
94 /** The TCP socket handle for the connection. */
95 RTSOCKET hSock;
96 /** The TCP server. */
97 PRTTCPSERVER pTcpSrv;
98} RTTRACELOGTOOLTCP;
99/** Pointer to the TCP server/client state. */
100typedef RTTRACELOGTOOLTCP *PRTTRACELOGTOOLTCP;
101
102
103static void rtTraceLogTcpDestroy(PRTTRACELOGTOOLTCP pTrcLogTcp)
104{
105 if (pTrcLogTcp->fIsServer)
106 RTTcpServerDestroy(pTrcLogTcp->pTcpSrv);
107 if (pTrcLogTcp->hSock != NIL_RTSOCKET)
108 {
109 if (pTrcLogTcp->fIsServer)
110 RTTcpServerDisconnectClient2(pTrcLogTcp->hSock);
111 else
112 RTTcpClientClose(pTrcLogTcp->hSock);
113 }
114 RTMemFree(pTrcLogTcp);
115}
116
117
118static DECLCALLBACK(int) rtTraceLogToolTcpInput(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbRead,
119 RTMSINTERVAL cMsTimeout)
120{
121 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)pvUser;
122 if ( pTrcLogTcp->fIsServer
123 && pTrcLogTcp->hSock == NIL_RTSOCKET)
124 {
125 int rc = RTTcpServerListen2(pTrcLogTcp->pTcpSrv, &pTrcLogTcp->hSock);
126 if (RT_FAILURE(rc))
127 return rc;
128 }
129
130 int rc = RTTcpSelectOne(pTrcLogTcp->hSock, cMsTimeout);
131 if (RT_SUCCESS(rc))
132 rc = RTTcpReadNB(pTrcLogTcp->hSock, pvBuf, cbBuf, pcbRead);
133
134 return rc;
135}
136
137
138static DECLCALLBACK(int) rtTraceLogToolTcpClose(void *pvUser)
139{
140 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)pvUser;
141 rtTraceLogTcpDestroy(pTrcLogTcp);
142 return VINF_SUCCESS;
143}
144
145
146/**
147 * Tries to create a new trace log reader using the given input.
148 *
149 * @returns IPRT status code.
150 * @param phTraceLogRdr Where to store the handle to the trace log reader instance on success.
151 * @param pszInput The input path.
152 * @param pszSave The optional path to save
153 */
154static int rtTraceLogToolReaderCreate(PRTTRACELOGRDR phTraceLogRdr, const char *pszInput, const char *pszSave)
155{
156 RT_NOREF(pszSave);
157
158 /* Try treating the input as a file first. */
159 int rc = RTTraceLogRdrCreateFromFile(phTraceLogRdr, pszInput);
160 if (RT_FAILURE(rc))
161 {
162 /*
163 * Check whether the input looks like a port number or an address:port pair.
164 * The former will create a server listening on the port while the latter tries
165 * to connect to the given address:port combination.
166 */
167 uint32_t uPort = 0;
168 bool fIsServer = false;
169 PRTTCPSERVER pTcpSrv = NULL;
170 RTSOCKET hSock = NIL_RTSOCKET;
171 rc = RTStrToUInt32Full(pszInput, 10, &uPort);
172 if (rc == VINF_SUCCESS)
173 {
174 fIsServer = true;
175 rc = RTTcpServerCreateEx(NULL, uPort, &pTcpSrv);
176 }
177 else
178 {
179 /* Try treating the input as an address:port pair. */
180 }
181
182 if (RT_SUCCESS(rc))
183 {
184 /* Initialize structure and reader. */
185 PRTTRACELOGTOOLTCP pTrcLogTcp = (PRTTRACELOGTOOLTCP)RTMemAllocZ(sizeof(*pTrcLogTcp));
186 if (pTrcLogTcp)
187 {
188 pTrcLogTcp->fIsServer = fIsServer;
189 pTrcLogTcp->hSock = hSock;
190 pTrcLogTcp->pTcpSrv = pTcpSrv;
191 rc = RTTraceLogRdrCreate(phTraceLogRdr, rtTraceLogToolTcpInput, rtTraceLogToolTcpClose, pTrcLogTcp);
192 if (RT_FAILURE(rc))
193 rtTraceLogTcpDestroy(pTrcLogTcp);
194 }
195 else
196 {
197 if (fIsServer)
198 RTTcpServerDestroy(pTcpSrv);
199 else
200 RTSocketClose(hSock);
201 }
202 }
203 }
204 return rc;
205}
206
207
208static DECLCALLBACK(int) rtTraceLogToolDecoderHlpPrintf(PRTTRACELOGDECODERHLP pHlp, const char *pszFormat, ...)
209{
210 RT_NOREF(pHlp);
211 va_list Args;
212 va_start(Args, pszFormat);
213 int rc = RTMsgInfoV(pszFormat, Args);
214 va_end(Args);
215 return rc;
216}
217
218
219static DECLCALLBACK(int) rtTraceLogToolDecoderHlpErrorMsg(PRTTRACELOGDECODERHLP pHlp, const char *pszFormat, ...)
220{
221 RT_NOREF(pHlp);
222 va_list Args;
223 va_start(Args, pszFormat);
224 int rc = RTMsgErrorV(pszFormat, Args);
225 va_end(Args);
226 return rc;
227}
228
229
230static DECLCALLBACK(int) rtTraceLogToolDecoderHlpStateCreate(PRTTRACELOGDECODERHLP pHlp, size_t cbState, PFNTRACELOGDECODERSTATEFREE pfnFree,
231 void **ppvState)
232{
233 PRTTRACELOGDECODER pDecoder = RT_FROM_MEMBER(pHlp, RTTRACELOGDECODER, Hlp);
234
235 if (pDecoder->pvDecoderState)
236 {
237 if (pDecoder->pfnDecoderStateFree)
238 pDecoder->pfnDecoderStateFree(pHlp, pDecoder->pvDecoderState);
239 RTMemFree(pDecoder->pvDecoderState);
240 pDecoder->pvDecoderState = NULL;
241 pDecoder->pfnDecoderStateFree = NULL;
242 }
243
244 pDecoder->pvDecoderState = RTMemAllocZ(cbState);
245 if (pDecoder->pvDecoderState)
246 {
247 pDecoder->pfnDecoderStateFree = pfnFree;
248 *ppvState = pDecoder->pvDecoderState;
249 return VINF_SUCCESS;
250 }
251
252 return VERR_NO_MEMORY;
253}
254
255
256static DECLCALLBACK(void) rtTraceLogToolDecoderHlpStateDestroy(PRTTRACELOGDECODERHLP pHlp)
257{
258 PRTTRACELOGDECODER pDecoder = RT_FROM_MEMBER(pHlp, RTTRACELOGDECODER, Hlp);
259
260 if (pDecoder->pvDecoderState)
261 {
262 if (pDecoder->pfnDecoderStateFree)
263 pDecoder->pfnDecoderStateFree(pHlp, pDecoder->pvDecoderState);
264 RTMemFree(pDecoder->pvDecoderState);
265 pDecoder->pvDecoderState = NULL;
266 pDecoder->pfnDecoderStateFree = NULL;
267 }
268}
269
270
271static DECLCALLBACK(void*) rtTraceLogToolDecoderHlpStateGet(PRTTRACELOGDECODERHLP pHlp)
272{
273 PRTTRACELOGDECODER pDecoder = RT_FROM_MEMBER(pHlp, RTTRACELOGDECODER, Hlp);
274
275 return pDecoder->pvDecoderState;
276}
277
278
279static DECLCALLBACK(int) rtTraceLogToolRegisterDecoders(void *pvUser, PCRTTRACELOGDECODERREG paDecoders, uint32_t cDecoders)
280{
281 PRTTRACELOGDECODERSTATE pDecoderState = (PRTTRACELOGDECODERSTATE)pvUser;
282
283 if (pDecoderState->cDecodersAlloc - pDecoderState->cDecoders <= cDecoders)
284 {
285 PRTTRACELOGDECODER paNew = (PRTTRACELOGDECODER)RTMemRealloc(pDecoderState->paDecoders,
286 (pDecoderState->cDecodersAlloc + cDecoders) * sizeof(*paNew));
287 if (!paNew)
288 return VERR_NO_MEMORY;
289
290 pDecoderState->paDecoders = paNew;
291 pDecoderState->cDecodersAlloc += cDecoders;
292 }
293
294 for (uint32_t i = 0; i < cDecoders; i++)
295 {
296 PRTTRACELOGDECODER pDecoder = &pDecoderState->paDecoders[i];
297
298 pDecoder->pReg = &paDecoders[i];
299 pDecoder->pvDecoderState = NULL;
300 pDecoder->pfnDecoderStateFree = NULL;
301 pDecoder->Hlp.pfnPrintf = rtTraceLogToolDecoderHlpPrintf;
302 pDecoder->Hlp.pfnErrorMsg = rtTraceLogToolDecoderHlpErrorMsg;
303 pDecoder->Hlp.pfnDecoderStateCreate = rtTraceLogToolDecoderHlpStateCreate;
304 pDecoder->Hlp.pfnDecoderStateDestroy = rtTraceLogToolDecoderHlpStateDestroy;
305 pDecoder->Hlp.pfnDecoderStateGet = rtTraceLogToolDecoderHlpStateGet;
306 }
307
308 pDecoderState->cDecoders += cDecoders;
309 return VINF_SUCCESS;
310}
311
312
313int main(int argc, char **argv)
314{
315 int rc = RTR3InitExe(argc, &argv, 0);
316 if (RT_FAILURE(rc))
317 return RTMsgInitFailure(rc);
318
319 /*
320 * Parse arguments.
321 */
322 static const RTGETOPTDEF s_aOptions[] =
323 {
324 { "--input", 'i', RTGETOPT_REQ_STRING },
325 { "--save", 's', RTGETOPT_REQ_STRING },
326 { "--load-decoder", 'l', RTGETOPT_REQ_STRING },
327 { "--help", 'h', RTGETOPT_REQ_NOTHING },
328 { "--version", 'V', RTGETOPT_REQ_NOTHING },
329 };
330
331 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
332 const char *pszInput = NULL;
333 const char *pszSave = NULL;
334 RTTRACELOGDECODERSTATE DecoderState; RT_ZERO(DecoderState);
335
336 RTGETOPTUNION ValueUnion;
337 RTGETOPTSTATE GetState;
338 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
339 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
340 {
341 switch (rc)
342 {
343 case 'h':
344 RTPrintf("Usage: %s [options]\n"
345 "\n"
346 "Options:\n"
347 " -i,--input=<file|port|address:port>\n"
348 " Input path, can be a file a port to start listening on for incoming connections or an address:port to connect to\n"
349 " -s,--save=file\n"
350 " Save the input to a file for later use\n"
351 " -l,--load-decoder=<plugin path>\n"
352 " Loads the given decoder library used for decoding events\n"
353 " -h, -?, --help\n"
354 " Display this help text and exit successfully.\n"
355 " -V, --version\n"
356 " Display the revision and exit successfully.\n"
357 , RTPathFilename(argv[0]));
358 return RTEXITCODE_SUCCESS;
359 case 'V':
360 RTPrintf("$Revision: 104921 $\n");
361 return RTEXITCODE_SUCCESS;
362
363 case 'i':
364 pszInput = ValueUnion.psz;
365 break;
366 case 's':
367 pszSave = ValueUnion.psz;
368 break;
369 case 'l':
370 {
371 RTLDRMOD hLdrMod;
372 rc = RTLdrLoadEx(ValueUnion.psz, &hLdrMod, RTLDRLOAD_FLAGS_NO_UNLOAD, NULL);
373 if (RT_SUCCESS(rc))
374 {
375 PFNTRACELOGDECODERPLUGINLOAD pfnLoad = NULL;
376 rc = RTLdrGetSymbol(hLdrMod, RT_TRACELOG_DECODER_PLUGIN_LOAD, (void **)&pfnLoad);
377 if (RT_SUCCESS(rc))
378 {
379 RTTRACELOGDECODERREGISTER RegCb;
380
381 RegCb.u32Version = RT_TRACELOG_DECODERREG_CB_VERSION;
382 RegCb.pfnRegisterDecoders = rtTraceLogToolRegisterDecoders;
383
384 rc = pfnLoad(&DecoderState, &RegCb);
385 if (RT_FAILURE(rc))
386 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to register decoders %Rrc\n", rc);
387 }
388 else
389 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to lretrieve entry point '%s' %Rrc\n",
390 RT_TRACELOG_DECODER_PLUGIN_LOAD, rc);
391
392 RTLdrClose(hLdrMod);
393 }
394 else
395 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to load decoder library %Rrc\n", rc);
396 break;
397 }
398 default:
399 return RTGetOptPrintError(rc, &ValueUnion);
400 }
401 }
402
403 if (!pszInput)
404 {
405 RTPrintf("An input path must be given\n");
406 return RTEXITCODE_FAILURE;
407 }
408
409 /*
410 * Create trace log reader instance.
411 */
412 RTTRACELOGRDR hTraceLogRdr = NIL_RTTRACELOGRDR;
413 rc = rtTraceLogToolReaderCreate(&hTraceLogRdr, pszInput, pszSave);
414 if (RT_SUCCESS(rc))
415 {
416 do
417 {
418 RTTRACELOGRDRPOLLEVT enmEvt = RTTRACELOGRDRPOLLEVT_INVALID;
419 rc = RTTraceLogRdrEvtPoll(hTraceLogRdr, &enmEvt, RT_INDEFINITE_WAIT);
420 if (RT_SUCCESS(rc))
421 {
422 switch (enmEvt)
423 {
424 case RTTRACELOGRDRPOLLEVT_HDR_RECVD:
425 RTMsgInfo("A valid header was received\n");
426 break;
427 case RTTRACELOGRDRPOLLEVT_TRACE_EVENT_RECVD:
428 {
429 RTTRACELOGRDREVT hTraceLogEvt;
430 rc = RTTraceLogRdrQueryLastEvt(hTraceLogRdr, &hTraceLogEvt);
431 if (RT_SUCCESS(rc))
432 {
433 PCRTTRACELOGEVTDESC pEvtDesc = RTTraceLogRdrEvtGetDesc(hTraceLogEvt);
434 RTMsgInfo("%llu %llu %s\n",
435 RTTraceLogRdrEvtGetSeqNo(hTraceLogEvt),
436 RTTraceLogRdrEvtGetTs(hTraceLogEvt),
437 pEvtDesc->pszId);
438
439 /*
440 * Look through our registered decoders and pass the decoding on to it.
441 * If there is no decoder registered just dump the raw values.
442 */
443 PRTTRACELOGDECODER pDecoder = NULL;
444 PCRTTRACELOGDECODEEVT pDecodeEvt = NULL;
445 for (uint32_t i = 0; (i < DecoderState.cDecoders) && !pDecoder; i++)
446 {
447 PCRTTRACELOGDECODEEVT pTmp = DecoderState.paDecoders[i].pReg->paEvtIds;
448 while (pTmp->pszEvtId)
449 {
450 if (!strcmp(pTmp->pszEvtId, pEvtDesc->pszId))
451 {
452 pDecoder = &DecoderState.paDecoders[i];
453 pDecodeEvt = pTmp;
454 break;
455 }
456 pTmp++;
457 }
458 }
459
460 if (pDecoder)
461 {
462 Assert(pDecodeEvt);
463
464 /** @todo Dynamic value allocation (too lazy right now). */
465 RTTRACELOGEVTVAL aVals[32];
466 uint32_t cVals = 0;
467 rc = RTTraceLogRdrEvtFillVals(hTraceLogEvt, 0, &aVals[0], RT_ELEMENTS(aVals),
468 &cVals);
469 if ( RT_SUCCESS(rc)
470 || cVals != pEvtDesc->cEvtItems)
471 {
472 rc = pDecoder->pReg->pfnDecode(&pDecoder->Hlp, pDecodeEvt->idDecodeEvt, hTraceLogEvt,
473 pEvtDesc, &aVals[0], cVals);
474 if (RT_FAILURE(rc))
475 RTMsgError("Failed to decode event with ID '%s' -> %Rrc\n", pEvtDesc->pszId, rc);
476 }
477 else
478 RTMsgError("Failed to fill values for event with ID '%s' -> %Rrc (cVals=%u vs. cEvtItems=%u)\n",
479 pEvtDesc->pszId, rc, cVals, pEvtDesc->cEvtItems);
480 }
481 else
482 for (unsigned i = 0; i < pEvtDesc->cEvtItems; i++)
483 {
484 RTTRACELOGEVTVAL Val;
485 unsigned cVals = 0;
486 rc = RTTraceLogRdrEvtFillVals(hTraceLogEvt, i, &Val, 1, &cVals);
487 if (RT_SUCCESS(rc))
488 {
489 switch (Val.pItemDesc->enmType)
490 {
491 case RTTRACELOGTYPE_BOOL:
492 RTMsgInfo(" %s: %s\n", Val.pItemDesc->pszName, Val.u.f ? "true" : "false");
493 break;
494 case RTTRACELOGTYPE_UINT8:
495 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u8);
496 break;
497 case RTTRACELOGTYPE_INT8:
498 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i8);
499 break;
500 case RTTRACELOGTYPE_UINT16:
501 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u16);
502 break;
503 case RTTRACELOGTYPE_INT16:
504 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i16);
505 break;
506 case RTTRACELOGTYPE_UINT32:
507 RTMsgInfo(" %s: %u\n", Val.pItemDesc->pszName, Val.u.u32);
508 break;
509 case RTTRACELOGTYPE_INT32:
510 RTMsgInfo(" %s: %d\n", Val.pItemDesc->pszName, Val.u.i32);
511 break;
512 case RTTRACELOGTYPE_UINT64:
513 RTMsgInfo(" %s: %llu\n", Val.pItemDesc->pszName, Val.u.u64);
514 break;
515 case RTTRACELOGTYPE_INT64:
516 RTMsgInfo(" %s: %lld\n", Val.pItemDesc->pszName, Val.u.i64);
517 break;
518 case RTTRACELOGTYPE_RAWDATA:
519 RTMsgInfo(" %s:\n"
520 "%.*Rhxd\n", Val.pItemDesc->pszName, Val.u.RawData.cb, Val.u.RawData.pb);
521 break;
522 case RTTRACELOGTYPE_FLOAT32:
523 case RTTRACELOGTYPE_FLOAT64:
524 RTMsgInfo(" %s: Float32 and Float64 data not supported yet\n", Val.pItemDesc->pszName);
525 break;
526 case RTTRACELOGTYPE_POINTER:
527 RTMsgInfo(" %s: %#llx\n", Val.pItemDesc->pszName, Val.u.uPtr);
528 break;
529 case RTTRACELOGTYPE_SIZE:
530 RTMsgInfo(" %s: %llu\n", Val.pItemDesc->pszName, Val.u.sz);
531 break;
532 default:
533 RTMsgError(" %s: Invalid type given %d\n", Val.pItemDesc->pszName, Val.pItemDesc->enmType);
534 }
535 }
536 else
537 RTMsgInfo(" Failed to retrieve event data with %Rrc\n", rc);
538 }
539 }
540 break;
541 }
542 default:
543 RTMsgInfo("Invalid event received: %d\n", enmEvt);
544 }
545 }
546 else
547 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Polling for an event failed with %Rrc\n", rc);
548 } while (RT_SUCCESS(rc));
549
550 RTTraceLogRdrDestroy(hTraceLogRdr);
551 }
552 else
553 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create trace log reader with %Rrc\n", rc);
554
555 return rcExit;
556}
557
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette