VirtualBox

source: vbox/trunk/src/VBox/Main/Performance.cpp@ 29034

Last change on this file since 29034 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1/* $Id: Performance.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2
3/** @file
4 *
5 * VBox Performance Classes implementation.
6 */
7
8/*
9 * Copyright (C) 2008 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20/*
21 * @todo list:
22 *
23 * 1) Detection of erroneous metric names
24 */
25
26#ifndef VBOX_COLLECTOR_TEST_CASE
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#endif
30#include "Performance.h"
31
32#include <VBox/com/array.h>
33#include <VBox/com/ptr.h>
34#include <VBox/com/string.h>
35#include <VBox/err.h>
36#include <iprt/string.h>
37#include <iprt/mem.h>
38#include <iprt/cpuset.h>
39
40#include <algorithm>
41
42#include "Logging.h"
43
44using namespace pm;
45
46// Stubs for non-pure virtual methods
47
48int CollectorHAL::getHostCpuLoad(ULONG * /* user */, ULONG * /* kernel */, ULONG * /* idle */)
49{
50 return E_NOTIMPL;
51}
52
53int CollectorHAL::getProcessCpuLoad(RTPROCESS /* process */, ULONG * /* user */, ULONG * /* kernel */)
54{
55 return E_NOTIMPL;
56}
57
58int CollectorHAL::getRawHostCpuLoad(uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* idle */)
59{
60 return E_NOTIMPL;
61}
62
63int CollectorHAL::getRawProcessCpuLoad(RTPROCESS /* process */, uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* total */)
64{
65 return E_NOTIMPL;
66}
67
68int CollectorHAL::getHostMemoryUsage(ULONG * /* total */, ULONG * /* used */, ULONG * /* available */)
69{
70 return E_NOTIMPL;
71}
72
73int CollectorHAL::getProcessMemoryUsage(RTPROCESS /* process */, ULONG * /* used */)
74{
75 return E_NOTIMPL;
76}
77
78int CollectorHAL::enable()
79{
80 return E_NOTIMPL;
81}
82
83int CollectorHAL::disable()
84{
85 return E_NOTIMPL;
86}
87
88/* Generic implementations */
89
90int CollectorHAL::getHostCpuMHz(ULONG *mhz)
91{
92 unsigned cCpus = 0;
93 uint64_t u64TotalMHz = 0;
94 RTCPUSET OnlineSet;
95 RTMpGetOnlineSet(&OnlineSet);
96 for (RTCPUID iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
97 {
98 LogAleksey(("{%p} " LOG_FN_FMT ": Checking if CPU %d is member of online set...\n",
99 this, __PRETTY_FUNCTION__, (int)iCpu));
100 if (RTCpuSetIsMemberByIndex(&OnlineSet, iCpu))
101 {
102 LogAleksey(("{%p} " LOG_FN_FMT ": Getting frequency for CPU %d...\n",
103 this, __PRETTY_FUNCTION__, (int)iCpu));
104 uint32_t uMHz = RTMpGetCurFrequency(RTMpCpuIdFromSetIndex(iCpu));
105 if (uMHz != 0)
106 {
107 LogAleksey(("{%p} " LOG_FN_FMT ": CPU %d %u MHz\n",
108 this, __PRETTY_FUNCTION__, (int)iCpu, uMHz));
109 u64TotalMHz += uMHz;
110 cCpus++;
111 }
112 }
113 }
114
115 AssertReturn(cCpus, VERR_NOT_IMPLEMENTED);
116 *mhz = (ULONG)(u64TotalMHz / cCpus);
117
118 return VINF_SUCCESS;
119}
120
121#ifndef VBOX_COLLECTOR_TEST_CASE
122CollectorGuestHAL::~CollectorGuestHAL()
123{
124 Assert(!cEnabled);
125}
126
127int CollectorGuestHAL::enable()
128{
129 HRESULT ret = S_OK;
130
131 if (ASMAtomicIncU32(&cEnabled) == 1)
132 {
133 ComPtr<IInternalSessionControl> directControl;
134
135 ret = mMachine->getDirectControl(&directControl);
136 if (ret != S_OK)
137 return ret;
138
139 /* get the associated console; this is a remote call (!) */
140 ret = directControl->GetRemoteConsole(mConsole.asOutParam());
141 if (ret != S_OK)
142 return ret;
143
144 ret = mConsole->COMGETTER(Guest)(mGuest.asOutParam());
145 if (ret == S_OK)
146 mGuest->COMSETTER(StatisticsUpdateInterval)(1 /* 1 sec */);
147 }
148 return ret;
149}
150
151int CollectorGuestHAL::disable()
152{
153 if (ASMAtomicDecU32(&cEnabled) == 0)
154 {
155 Assert(mGuest && mConsole);
156 mGuest->COMSETTER(StatisticsUpdateInterval)(0 /* off */);
157 }
158 return S_OK;
159}
160
161int CollectorGuestHAL::preCollect(const CollectorHints& /* hints */, uint64_t iTick)
162{
163 if ( mGuest
164 && iTick != mLastTick)
165 {
166 ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal;
167
168 mGuest->InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
169 &mMemTotal, &mMemFree, &mMemBalloon, &mMemCache,
170 &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal, &ulMemBalloonTotal);
171
172 if (mHostHAL)
173 mHostHAL->setMemHypervisorStats(ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal);
174
175 mLastTick = iTick;
176 }
177 return S_OK;
178}
179
180#endif /* !VBOX_COLLECTOR_TEST_CASE */
181
182bool BaseMetric::collectorBeat(uint64_t nowAt)
183{
184 if (isEnabled())
185 {
186 if (nowAt - mLastSampleTaken >= mPeriod * 1000)
187 {
188 mLastSampleTaken = nowAt;
189 Log4(("{%p} " LOG_FN_FMT ": Collecting %s for obj(%p)...\n",
190 this, __PRETTY_FUNCTION__, getName(), (void *)mObject));
191 return true;
192 }
193 }
194 return false;
195}
196
197/*bool BaseMetric::associatedWith(ComPtr<IUnknown> object)
198{
199 LogFlowThisFunc(("mObject(%p) == object(%p) is %s.\n", mObject, object, mObject == object ? "true" : "false"));
200 return mObject == object;
201}*/
202
203void HostCpuLoad::init(ULONG period, ULONG length)
204{
205 mPeriod = period;
206 mLength = length;
207 mUser->init(mLength);
208 mKernel->init(mLength);
209 mIdle->init(mLength);
210}
211
212void HostCpuLoad::collect()
213{
214 ULONG user, kernel, idle;
215 int rc = mHAL->getHostCpuLoad(&user, &kernel, &idle);
216 if (RT_SUCCESS(rc))
217 {
218 mUser->put(user);
219 mKernel->put(kernel);
220 mIdle->put(idle);
221 }
222}
223
224void HostCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
225{
226 hints.collectHostCpuLoad();
227}
228
229void HostCpuLoadRaw::collect()
230{
231 uint64_t user, kernel, idle;
232 uint64_t userDiff, kernelDiff, idleDiff, totalDiff;
233
234 int rc = mHAL->getRawHostCpuLoad(&user, &kernel, &idle);
235 if (RT_SUCCESS(rc))
236 {
237 userDiff = user - mUserPrev;
238 kernelDiff = kernel - mKernelPrev;
239 idleDiff = idle - mIdlePrev;
240 totalDiff = userDiff + kernelDiff + idleDiff;
241
242 if (totalDiff == 0)
243 {
244 /* This is only possible if none of counters has changed! */
245 LogFlowThisFunc(("Impossible! User, kernel and idle raw "
246 "counters has not changed since last sample.\n" ));
247 mUser->put(0);
248 mKernel->put(0);
249 mIdle->put(0);
250 }
251 else
252 {
253 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * userDiff / totalDiff));
254 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * kernelDiff / totalDiff));
255 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * idleDiff / totalDiff));
256 }
257
258 mUserPrev = user;
259 mKernelPrev = kernel;
260 mIdlePrev = idle;
261 }
262}
263
264void HostCpuMhz::init(ULONG period, ULONG length)
265{
266 mPeriod = period;
267 mLength = length;
268 mMHz->init(mLength);
269}
270
271void HostCpuMhz::collect()
272{
273 ULONG mhz;
274 int rc = mHAL->getHostCpuMHz(&mhz);
275 if (RT_SUCCESS(rc))
276 mMHz->put(mhz);
277}
278
279void HostRamUsage::init(ULONG period, ULONG length)
280{
281 mPeriod = period;
282 mLength = length;
283 mTotal->init(mLength);
284 mUsed->init(mLength);
285 mAvailable->init(mLength);
286 mAllocVMM->init(mLength);
287 mFreeVMM->init(mLength);
288 mBalloonVMM->init(mLength);
289}
290
291void HostRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
292{
293 hints.collectHostRamUsage();
294}
295
296void HostRamUsage::collect()
297{
298 ULONG total, used, available;
299 int rc = mHAL->getHostMemoryUsage(&total, &used, &available);
300 if (RT_SUCCESS(rc))
301 {
302 mTotal->put(total);
303 mUsed->put(used);
304 mAvailable->put(available);
305
306 }
307 ULONG allocVMM, freeVMM, balloonVMM;
308
309 mHAL->getMemHypervisorStats(&allocVMM, &freeVMM, &balloonVMM);
310 mAllocVMM->put(allocVMM);
311 mFreeVMM->put(freeVMM);
312 mBalloonVMM->put(balloonVMM);
313}
314
315
316
317void MachineCpuLoad::init(ULONG period, ULONG length)
318{
319 mPeriod = period;
320 mLength = length;
321 mUser->init(mLength);
322 mKernel->init(mLength);
323}
324
325void MachineCpuLoad::collect()
326{
327 ULONG user, kernel;
328 int rc = mHAL->getProcessCpuLoad(mProcess, &user, &kernel);
329 if (RT_SUCCESS(rc))
330 {
331 mUser->put(user);
332 mKernel->put(kernel);
333 }
334}
335
336void MachineCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */)
337{
338 hints.collectProcessCpuLoad(mProcess);
339}
340
341void MachineCpuLoadRaw::collect()
342{
343 uint64_t processUser, processKernel, hostTotal;
344
345 int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
346 if (RT_SUCCESS(rc))
347 {
348 if (hostTotal == mHostTotalPrev)
349 {
350 /* Nearly impossible, but... */
351 mUser->put(0);
352 mKernel->put(0);
353 }
354 else
355 {
356 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
357 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
358 }
359
360 mHostTotalPrev = hostTotal;
361 mProcessUserPrev = processUser;
362 mProcessKernelPrev = processKernel;
363 }
364}
365
366void MachineRamUsage::init(ULONG period, ULONG length)
367{
368 mPeriod = period;
369 mLength = length;
370 mUsed->init(mLength);
371}
372
373void MachineRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */)
374{
375 hints.collectProcessRamUsage(mProcess);
376}
377
378void MachineRamUsage::collect()
379{
380 ULONG used;
381 int rc = mHAL->getProcessMemoryUsage(mProcess, &used);
382 if (RT_SUCCESS(rc))
383 mUsed->put(used);
384}
385
386
387void GuestCpuLoad::init(ULONG period, ULONG length)
388{
389 mPeriod = period;
390 mLength = length;
391
392 mUser->init(mLength);
393 mKernel->init(mLength);
394 mIdle->init(mLength);
395}
396
397void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t iTick)
398{
399 mHAL->preCollect(hints, iTick);
400}
401
402void GuestCpuLoad::collect()
403{
404 ULONG CpuUser = 0, CpuKernel = 0, CpuIdle = 0;
405
406 mGuestHAL->getGuestCpuLoad(&CpuUser, &CpuKernel, &CpuIdle);
407 mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * CpuUser) / 100);
408 mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * CpuKernel) / 100);
409 mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * CpuIdle) / 100);
410}
411
412void GuestRamUsage::init(ULONG period, ULONG length)
413{
414 mPeriod = period;
415 mLength = length;
416
417 mTotal->init(mLength);
418 mFree->init(mLength);
419 mBallooned->init(mLength);
420 mCache->init(mLength);
421 mPagedTotal->init(mLength);
422}
423
424void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t iTick)
425{
426 mHAL->preCollect(hints, iTick);
427}
428
429void GuestRamUsage::collect()
430{
431 ULONG ulMemTotal = 0, ulMemFree = 0, ulMemBalloon = 0, ulMemCache = 0, ulPageTotal = 0;
432
433 mGuestHAL->getGuestMemLoad(&ulMemTotal, &ulMemFree, &ulMemBalloon, &ulMemCache, &ulPageTotal);
434 mTotal->put(ulMemTotal);
435 mFree->put(ulMemFree);
436 mBallooned->put(ulMemBalloon);
437 mCache->put(ulMemCache);
438 mPagedTotal->put(ulPageTotal);
439}
440
441void CircularBuffer::init(ULONG ulLength)
442{
443 if (mData)
444 RTMemFree(mData);
445 mLength = ulLength;
446 if (mLength)
447 mData = (ULONG*)RTMemAllocZ(ulLength * sizeof(ULONG));
448 else
449 mData = NULL;
450 mWrapped = false;
451 mEnd = 0;
452 mSequenceNumber = 0;
453}
454
455ULONG CircularBuffer::length()
456{
457 return mWrapped ? mLength : mEnd;
458}
459
460void CircularBuffer::put(ULONG value)
461{
462 if (mData)
463 {
464 mData[mEnd++] = value;
465 if (mEnd >= mLength)
466 {
467 mEnd = 0;
468 mWrapped = true;
469 }
470 ++mSequenceNumber;
471 }
472}
473
474void CircularBuffer::copyTo(ULONG *data)
475{
476 if (mWrapped)
477 {
478 memcpy(data, mData + mEnd, (mLength - mEnd) * sizeof(ULONG));
479 // Copy the wrapped part
480 if (mEnd)
481 memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(ULONG));
482 }
483 else
484 memcpy(data, mData, mEnd * sizeof(ULONG));
485}
486
487void SubMetric::query(ULONG *data)
488{
489 copyTo(data);
490}
491
492void Metric::query(ULONG **data, ULONG *count, ULONG *sequenceNumber)
493{
494 ULONG length;
495 ULONG *tmpData;
496
497 length = mSubMetric->length();
498 *sequenceNumber = mSubMetric->getSequenceNumber() - length;
499 if (length)
500 {
501 tmpData = (ULONG*)RTMemAlloc(sizeof(*tmpData)*length);
502 mSubMetric->query(tmpData);
503 if (mAggregate)
504 {
505 *count = 1;
506 *data = (ULONG*)RTMemAlloc(sizeof(**data));
507 **data = mAggregate->compute(tmpData, length);
508 RTMemFree(tmpData);
509 }
510 else
511 {
512 *count = length;
513 *data = tmpData;
514 }
515 }
516 else
517 {
518 *count = 0;
519 *data = 0;
520 }
521}
522
523ULONG AggregateAvg::compute(ULONG *data, ULONG length)
524{
525 uint64_t tmp = 0;
526 for (ULONG i = 0; i < length; ++i)
527 tmp += data[i];
528 return (ULONG)(tmp / length);
529}
530
531const char * AggregateAvg::getName()
532{
533 return "avg";
534}
535
536ULONG AggregateMin::compute(ULONG *data, ULONG length)
537{
538 ULONG tmp = *data;
539 for (ULONG i = 0; i < length; ++i)
540 if (data[i] < tmp)
541 tmp = data[i];
542 return tmp;
543}
544
545const char * AggregateMin::getName()
546{
547 return "min";
548}
549
550ULONG AggregateMax::compute(ULONG *data, ULONG length)
551{
552 ULONG tmp = *data;
553 for (ULONG i = 0; i < length; ++i)
554 if (data[i] > tmp)
555 tmp = data[i];
556 return tmp;
557}
558
559const char * AggregateMax::getName()
560{
561 return "max";
562}
563
564Filter::Filter(ComSafeArrayIn(IN_BSTR, metricNames),
565 ComSafeArrayIn(IUnknown *, objects))
566{
567 /*
568 * Let's work around null/empty safe array mess. I am not sure there is
569 * a way to pass null arrays via webservice, I haven't found one. So I
570 * guess the users will be forced to use empty arrays instead. Constructing
571 * an empty SafeArray is a bit awkward, so what we do in this method is
572 * actually convert null arrays to empty arrays and pass them down to
573 * init() method. If someone knows how to do it better, please be my guest,
574 * fix it.
575 */
576 if (ComSafeArrayInIsNull(metricNames))
577 {
578 com::SafeArray<BSTR> nameArray;
579 if (ComSafeArrayInIsNull(objects))
580 {
581 com::SafeIfaceArray<IUnknown> objectArray;
582 objectArray.reset(0);
583 init(ComSafeArrayAsInParam(nameArray),
584 ComSafeArrayAsInParam(objectArray));
585 }
586 else
587 {
588 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
589 init(ComSafeArrayAsInParam(nameArray),
590 ComSafeArrayAsInParam(objectArray));
591 }
592 }
593 else
594 {
595 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
596 if (ComSafeArrayInIsNull(objects))
597 {
598 com::SafeIfaceArray<IUnknown> objectArray;
599 objectArray.reset(0);
600 init(ComSafeArrayAsInParam(nameArray),
601 ComSafeArrayAsInParam(objectArray));
602 }
603 else
604 {
605 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
606 init(ComSafeArrayAsInParam(nameArray),
607 ComSafeArrayAsInParam(objectArray));
608 }
609 }
610}
611
612void Filter::init(ComSafeArrayIn(IN_BSTR, metricNames),
613 ComSafeArrayIn(IUnknown *, objects))
614{
615 com::SafeArray<IN_BSTR> nameArray(ComSafeArrayInArg(metricNames));
616 com::SafeIfaceArray<IUnknown> objectArray(ComSafeArrayInArg(objects));
617
618 if (!objectArray.size())
619 {
620 if (nameArray.size())
621 {
622 for (size_t i = 0; i < nameArray.size(); ++i)
623 processMetricList(com::Utf8Str(nameArray[i]), ComPtr<IUnknown>());
624 }
625 else
626 processMetricList("*", ComPtr<IUnknown>());
627 }
628 else
629 {
630 for (size_t i = 0; i < objectArray.size(); ++i)
631 switch (nameArray.size())
632 {
633 case 0:
634 processMetricList("*", objectArray[i]);
635 break;
636 case 1:
637 processMetricList(com::Utf8Str(nameArray[0]), objectArray[i]);
638 break;
639 default:
640 processMetricList(com::Utf8Str(nameArray[i]), objectArray[i]);
641 break;
642 }
643 }
644}
645
646void Filter::processMetricList(const com::Utf8Str &name, const ComPtr<IUnknown> object)
647{
648 size_t startPos = 0;
649
650 for (size_t pos = name.find(",");
651 pos != com::Utf8Str::npos;
652 pos = name.find(",", startPos))
653 {
654 mElements.push_back(std::make_pair(object, iprt::MiniString(name.substr(startPos, pos - startPos).c_str())));
655 startPos = pos + 1;
656 }
657 mElements.push_back(std::make_pair(object, iprt::MiniString(name.substr(startPos).c_str())));
658}
659
660/**
661 * The following method was borrowed from stamR3Match (VMM/STAM.cpp) and
662 * modified to handle the special case of trailing colon in the pattern.
663 *
664 * @returns True if matches, false if not.
665 * @param pszPat Pattern.
666 * @param pszName Name to match against the pattern.
667 * @param fSeenColon Seen colon (':').
668 */
669bool Filter::patternMatch(const char *pszPat, const char *pszName,
670 bool fSeenColon)
671{
672 /* ASSUMES ASCII */
673 for (;;)
674 {
675 char chPat = *pszPat;
676 switch (chPat)
677 {
678 default:
679 if (*pszName != chPat)
680 return false;
681 break;
682
683 case '*':
684 {
685 while ((chPat = *++pszPat) == '*' || chPat == '?')
686 /* nothing */;
687
688 /* Handle a special case, the mask terminating with a colon. */
689 if (chPat == ':')
690 {
691 if (!fSeenColon && !pszPat[1])
692 return !strchr(pszName, ':');
693 fSeenColon = true;
694 }
695
696 for (;;)
697 {
698 char ch = *pszName++;
699 if ( ch == chPat
700 && ( !chPat
701 || patternMatch(pszPat + 1, pszName, fSeenColon)))
702 return true;
703 if (!ch)
704 return false;
705 }
706 /* won't ever get here */
707 break;
708 }
709
710 case '?':
711 if (!*pszName)
712 return false;
713 break;
714
715 /* Handle a special case, the mask terminating with a colon. */
716 case ':':
717 if (!fSeenColon && !pszPat[1])
718 return !*pszName;
719 if (*pszName != ':')
720 return false;
721 fSeenColon = true;
722 break;
723
724 case '\0':
725 return !*pszName;
726 }
727 pszName++;
728 pszPat++;
729 }
730 return true;
731}
732
733bool Filter::match(const ComPtr<IUnknown> object, const iprt::MiniString &name) const
734{
735 ElementList::const_iterator it;
736
737 LogAleksey(("Filter::match(%p, %s)\n", static_cast<const IUnknown*> (object), name.c_str()));
738 for (it = mElements.begin(); it != mElements.end(); it++)
739 {
740 LogAleksey(("...matching against(%p, %s)\n", static_cast<const IUnknown*> ((*it).first), (*it).second.c_str()));
741 if ((*it).first.isNull() || (*it).first == object)
742 {
743 // Objects match, compare names
744 if (patternMatch((*it).second.c_str(), name.c_str()))
745 {
746 LogFlowThisFunc(("...found!\n"));
747 return true;
748 }
749 }
750 }
751 LogAleksey(("...no matches!\n"));
752 return false;
753}
754/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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