author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Mon, 06 Feb 2017 13:09:24 +0100 | |
changeset 340993 | 7a9666bfb431ba3f94ecbad92aea6690d39d4131 |
parent 340992 | 8be8dd1678a5199465a2960b982bf574deb05e9d (current diff) |
parent 340911 | 1cc159c7a0445ec51e335c8a1a1cceea7bbf8380 (diff) |
child 340994 | 3a1ed6f4ed6aba725e85f8170cc13a25ca02cfe3 |
push id | 86615 |
push user | kwierso@gmail.com |
push date | Tue, 07 Feb 2017 01:52:08 +0000 |
treeherder | mozilla-inbound@f0453084d86e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 54.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/devtools/client/locales/en-US/netmonitor.properties +++ b/devtools/client/locales/en-US/netmonitor.properties @@ -744,8 +744,12 @@ netmonitor.custom.send=Send # LOCALIZATION NOTE (netmonitor.custom.cancel): This is the label displayed # on the button which cancels and closes the custom request form netmonitor.custom.cancel=Cancel # LOCALIZATION NOTE (netmonitor.backButton): This is the label displayed # on the button which exists the performance statistics view netmonitor.backButton=Back + +# LOCALIZATION NOTE (netmonitor.headers.learnMore): This is the label displayed +# next to a header list item, with a link to external documentation +netmonitor.headers.learnMore=Learn More
new file mode 100644 --- /dev/null +++ b/devtools/client/netmonitor/shared/components/headers-mdn.js @@ -0,0 +1,119 @@ +/* this source code form is subject to the terms of the mozilla public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * A mapping of header names to external documentation. Any header included + * here will show a "Learn More" link alongside it. + */ + +"use strict"; + +var URL_DOMAIN = "https://developer.mozilla.org"; +const URL_PATH = "/en-US/docs/Web/HTTP/Headers/"; +const URL_PARAMS = + "?utm_source=mozilla&utm_medium=devtools-netmonitor&utm_campaign=default"; + +var SUPPORTED_HEADERS = [ + "Accept", + "Accept-Charset", + "Accept-Encoding", + "Accept-Language", + "Accept-Ranges", + "Access-Control-Allow-Credentials", + "Access-Control-Allow-Headers", + "Access-Control-Allow-Methods", + "Access-Control-Allow-Origin", + "Access-Control-Expose-Headers", + "Access-Control-Max-Age", + "Access-Control-Request-Headers", + "Access-Control-Request-Method", + "Age", + "Cache-Control", + "Connection", + "Content-Disposition", + "Content-Encoding", + "Content-Language", + "Content-Length", + "Content-Location", + "Content-Security-Policy", + "Content-Security-Policy-Report-Only", + "Content-Type", + "Cookie", + "Cookie2", + "DNT", + "Date", + "ETag", + "Expires", + "From", + "Host", + "If-Match", + "If-Modified-Since", + "If-None-Match", + "If-Range", + "If-Unmodified-Since", + "Keep-Alive", + "Last-Modified", + "Location", + "Origin", + "Pragma", + "Public-Key-Pins", + "Public-Key-Pins-Report-Only", + "Referer", + "Referrer-Policy", + "Retry-After", + "Server", + "Set-Cookie", + "Set-Cookie2", + "Strict-Transport-Security", + "TE", + "Tk", + "Trailer", + "Transfer-Encoding", + "Upgrade-Insecure-Requests", + "User-Agent", + "Vary", + "Via", + "Warning", + "X-Content-Type-Options", + "X-DNS-Prefetch-Control", + "X-Frame-Options", + "X-XSS-Protection" +]; + +/** + * Get the MDN URL for the specified header + * + * @param {string} Name of the header + * The baseURL to use. + * + * @return {string} + * The MDN URL for the header, or null if not available. + */ +exports.getURL = (header) => { + if (SUPPORTED_HEADERS.indexOf(header) === -1) { + return null; + } + + return URL_DOMAIN + URL_PATH + header + URL_PARAMS; +}; + +/** + * Use a different domain for the URLs. Used only for testing. + * + * @param {string} domain + * The domain to use. + */ +exports.setDomain = (domain) => { + URL_DOMAIN = domain; +}; + +/** + * Use a different list of supported headers. Used only for testing. + * + * @param {array} headers + * The supported headers to use. + */ +exports.setSupportedHeaders = (headers) => { + SUPPORTED_HEADERS = headers; +};
--- a/devtools/client/netmonitor/shared/components/headers-panel.js +++ b/devtools/client/netmonitor/shared/components/headers-panel.js @@ -10,21 +10,26 @@ const { createClass, createFactory, DOM, PropTypes, } = require("devtools/client/shared/vendor/react"); const { L10N } = require("../../l10n"); const { writeHeaderText } = require("../../request-utils"); const { getFormattedSize } = require("../../utils/format-utils"); +const Services = require("Services"); +const { gDevTools } = require("devtools/client/framework/devtools"); +const HeadersMDN = require("devtools/client/netmonitor/shared/components/headers-mdn"); +const { REPS, MODE } = require("devtools/client/shared/components/reps/load-reps"); +const Rep = createFactory(REPS.Rep); // Components const PropertiesView = createFactory(require("./properties-view")); -const { div, input, textarea } = DOM; +const { a, div, input, textarea } = DOM; const EDIT_AND_RESEND = L10N.getStr("netmonitor.summary.editAndResend"); const RAW_HEADERS = L10N.getStr("netmonitor.summary.rawHeaders"); const RAW_HEADERS_REQUEST = L10N.getStr("netmonitor.summary.rawHeaders.requestHeaders"); const RAW_HEADERS_RESPONSE = L10N.getStr("netmonitor.summary.rawHeaders.responseHeaders"); const HEADERS_EMPTY_TEXT = L10N.getStr("headersEmptyText"); const HEADERS_FILTER_TEXT = L10N.getStr("headersFilterText"); const REQUEST_HEADERS = L10N.getStr("requestHeaders"); const REQUEST_HEADERS_FROM_UPLOAD = L10N.getStr("requestHeadersFromUpload"); @@ -40,16 +45,17 @@ const SUMMARY_VERSION = L10N.getStr("net * Lists basic information about the request */ const HeadersPanel = createClass({ displayName: "HeadersPanel", propTypes: { cloneSelectedRequest: PropTypes.func.isRequired, request: PropTypes.object.isRequired, + renderValue: PropTypes.func }, getInitialState() { return { rawHeadersOpened: false, }; }, @@ -208,15 +214,54 @@ const HeadersPanel = createClass({ summaryStatus, summaryVersion, summaryRawHeaders, ), PropertiesView({ object, filterPlaceHolder: HEADERS_FILTER_TEXT, sectionNames: Object.keys(object), + renderValue }), ) ); } }); +function onLearnMoreClick(e, headerDocURL) { + e.stopPropagation(); + e.preventDefault(); + + let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); + win.openUILinkIn(headerDocURL, "tab"); +} + +function renderValue(props) { + const { member, value } = props; + + if (typeof value !== "string") { + return null; + } + + let headerDocURL = HeadersMDN.getURL(member.name); + + return ( + div({ className: "treeValueCellDivider" }, + Rep(Object.assign(props, { + // FIXME: A workaround for the issue in StringRep + // Force StringRep to crop the text everytime + member: Object.assign({}, member, { open: false }), + mode: MODE.TINY, + cropLimit: 60, + })), + headerDocURL ? + a({ + className: "learn-more-link", + title: headerDocURL, + onClick: (e) => onLearnMoreClick(e, headerDocURL), + }, `[${L10N.getStr("netmonitor.headers.learnMore")}]`) + : + null + ) + ); +} + module.exports = HeadersPanel;
--- a/devtools/client/netmonitor/shared/components/moz.build +++ b/devtools/client/netmonitor/shared/components/moz.build @@ -1,16 +1,17 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( 'cookies-panel.js', 'details-panel.js', 'editor.js', + 'headers-mdn.js', 'headers-panel.js', 'params-panel.js', 'preview-panel.js', 'properties-view.js', 'response-panel.js', 'security-panel.js', 'timings-panel.js', )
--- a/devtools/client/netmonitor/test/browser.ini +++ b/devtools/client/netmonitor/test/browser.ini @@ -93,16 +93,17 @@ subsuite = clipboard skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts [browser_net_copy_as_curl.js] subsuite = clipboard skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts [browser_net_cors_requests.js] [browser_net_cyrillic-01.js] [browser_net_cyrillic-02.js] [browser_net_frame.js] +[browser_net_header-docs.js] skip-if = (os == 'linux' && debug && bits == 32) # Bug 1321434 [browser_net_filter-01.js] skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439 [browser_net_filter-02.js] [browser_net_filter-03.js] [browser_net_filter-04.js] [browser_net_footer-summary.js] [browser_net_html-preview.js]
new file mode 100644 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_header-docs.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const HeadersMDN = require("devtools/client/netmonitor/shared/components/headers-mdn"); + +/** + * Tests if "Learn More" links are correctly displayed + * next to headers. + */ +add_task(function* () { + let { tab, monitor } = yield initNetMonitor(POST_DATA_URL); + info("Starting test... "); + + let { document, NetMonitorView } = monitor.panelWin; + let { RequestsMenu } = NetMonitorView; + + RequestsMenu.lazyUpdate = false; + + let wait = waitForNetworkEvents(monitor, 0, 2); + yield ContentTask.spawn(tab.linkedBrowser, {}, function* () { + content.wrappedJSObject.performRequests(); + }); + yield wait; + + let origItem = RequestsMenu.getItemAtIndex(0); + RequestsMenu.selectedItem = origItem; + + EventUtils.sendMouseEvent({ type: "click" }, + document.querySelectorAll(".request-list-item")[0]); + + testShowLearnMore(origItem); + + return teardown(monitor); + + /* + * Tests that a "Learn More" button is only shown if + * and only if a header is documented in MDN. + */ + function testShowLearnMore(data) { + document.querySelectorAll(".properties-view .treeRow.stringRow").forEach((rowEl, index) => { + let headerName = rowEl.querySelectorAll(".treeLabelCell .treeLabel")[0].textContent; + let headerDocURL = HeadersMDN.getURL(headerName); + let learnMoreEl = rowEl.querySelectorAll(".treeValueCell .learn-more-link"); + + if (headerDocURL === null) { + ok(learnMoreEl.length === 0, + "undocumented header does not include a \"Learn More\" button"); + } else { + ok(learnMoreEl[0].getAttribute("title") === headerDocURL, + "documented header includes a \"Learn More\" button with a link to MDN"); + } + }); + } +});
--- a/devtools/client/shared/components/tree/tree-view.css +++ b/devtools/client/shared/components/tree/tree-view.css @@ -72,16 +72,33 @@ text-decoration: underline; } /* Filtering */ .treeTable .treeRow.hidden { display: none; } +.treeTable .treeValueCellDivider { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +/* Learn More link */ +.treeTable .treeValueCell .learn-more-link { + color: var(--theme-highlight-blue); + cursor: pointer; + margin: 0 5px; +} + +.treeTable .treeValueCell .learn-more-link:hover { + text-decoration: underline; +} + /******************************************************************************/ /* Toggle Icon */ .treeTable .treeRow .treeIcon { height: 14px; width: 14px; font-size: 10px; /* Set the size of loading spinner */ display: inline-block;
--- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -62,52 +62,56 @@ static int32_t gTimeoutBucketingStrategy #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT uint32_t TimeoutManager::sNestingLevel = 0; namespace { // The number of queued runnables within the TabGroup ThrottledEventQueue // at which to begin applying back pressure to the window. -const uint32_t kThrottledEventQueueBackPressure = 5000; +#define DEFAULT_THROTTLED_EVENT_QUEUE_BACK_PRESSURE 5000 +static uint32_t gThrottledEventQueueBackPressure; // The amount of delay to apply to timers when back pressure is triggered. // As the length of the ThrottledEventQueue grows delay is increased. The // delay is scaled such that every kThrottledEventQueueBackPressure runnables // in the queue equates to an additional kBackPressureDelayMS. -const double kBackPressureDelayMS = 500; +#define DEFAULT_BACK_PRESSURE_DELAY_MS 250 +static uint32_t gBackPressureDelayMS; // This defines a limit for how much the delay must drop before we actually // reduce back pressure throttle amount. This makes the throttle delay // a bit "sticky" once we enter back pressure. -const double kBackPressureDelayReductionThresholdMS = 400; +#define DEFAULT_BACK_PRESSURE_DELAY_REDUCTION_THRESHOLD_MS 1000 +static uint32_t gBackPressureDelayReductionThresholdMS; // The minimum delay we can reduce back pressure to before we just floor // the value back to zero. This allows us to ensure that we can exit // back pressure event if there are always a small number of runnables // queued up. -const double kBackPressureDelayMinimumMS = 100; +#define DEFAULT_BACK_PRESSURE_DELAY_MINIMUM_MS 100 +static uint32_t gBackPressureDelayMinimumMS; // Convert a ThrottledEventQueue length to a timer delay in milliseconds. // This will return a value between 0 and INT32_MAX. int32_t CalculateNewBackPressureDelayMS(uint32_t aBacklogDepth) { double multiplier = static_cast<double>(aBacklogDepth) / - static_cast<double>(kThrottledEventQueueBackPressure); - double value = kBackPressureDelayMS * multiplier; + static_cast<double>(gThrottledEventQueueBackPressure); + double value = static_cast<double>(gBackPressureDelayMS) * multiplier; // Avoid overflow if (value > INT32_MAX) { value = INT32_MAX; } // Once we get close to an empty queue just floor the delay back to zero. // We want to ensure we don't get stuck in a condition where there is a // small amount of delay remaining due to an active, but reasonable, queue. - else if (value < kBackPressureDelayMinimumMS) { + else if (value < static_cast<double>(gBackPressureDelayMinimumMS)) { value = 0; } return static_cast<int32_t>(value); } } // anonymous namespace TimeoutManager::TimeoutManager(nsGlobalWindow& aWindow) @@ -148,16 +152,29 @@ TimeoutManager::Initialize() "dom.min_tracking_background_timeout_value", DEFAULT_MIN_TRACKING_BACKGROUND_TIMEOUT_VALUE); Preferences::AddIntVarCache(&gTimeoutBucketingStrategy, "dom.timeout_bucketing_strategy", TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY); Preferences::AddBoolVarCache(&gAnnotateTrackingChannels, "privacy.trackingprotection.annotate_channels", false); + + Preferences::AddUintVarCache(&gThrottledEventQueueBackPressure, + "dom.timeout.throttled_event_queue_back_pressure", + DEFAULT_THROTTLED_EVENT_QUEUE_BACK_PRESSURE); + Preferences::AddUintVarCache(&gBackPressureDelayMS, + "dom.timeout.back_pressure_delay_ms", + DEFAULT_BACK_PRESSURE_DELAY_MS); + Preferences::AddUintVarCache(&gBackPressureDelayReductionThresholdMS, + "dom.timeout.back_pressure_delay_reduction_threshold_ms", + DEFAULT_BACK_PRESSURE_DELAY_REDUCTION_THRESHOLD_MS); + Preferences::AddUintVarCache(&gBackPressureDelayMinimumMS, + "dom.timeout.back_pressure_delay_minimum_ms", + DEFAULT_BACK_PRESSURE_DELAY_MINIMUM_MS); } uint32_t TimeoutManager::GetTimeoutId(Timeout::Reason aReason) { switch (aReason) { case Timeout::Reason::eIdleCallbackTimeout: return ++mIdleCallbackTimeoutCounter; @@ -649,17 +666,17 @@ TimeoutManager::MaybeApplyBackPressure() return; } // Only begin back pressure if the window has greatly fallen behind the main // thread. This is a somewhat arbitrary threshold chosen such that it should // rarely fire under normaly circumstances. Its low enough, though, // that we should have time to slow new runnables from being added before an // OOM occurs. - if (queue->Length() < kThrottledEventQueueBackPressure) { + if (queue->Length() < gThrottledEventQueueBackPressure) { return; } // First attempt to dispatch a runnable to update our back pressure state. We // do this first in order to verify we can dispatch successfully before // entering the back pressure state. nsCOMPtr<nsIRunnable> r = NewNonOwningRunnableMethod<StoreRefPtrPassByPtr<nsGlobalWindow>>(this, @@ -707,18 +724,18 @@ TimeoutManager::CancelOrUpdateBackPressu // If the delay has decreased, though, we only apply the new value if it has // reduced significantly. This hysteresis avoids thrashing the back pressure // value back and forth rapidly. This is important because reducing the // backpressure delay requires calling ResetTimerForThrottleReduction() which // can be quite expensive. We only want to call that method if the back log // is really clearing. else if (newBackPressureDelayMS == 0 || - (newBackPressureDelayMS <= - (mBackPressureDelayMS - kBackPressureDelayReductionThresholdMS))) { + (static_cast<uint32_t>(mBackPressureDelayMS) > + (newBackPressureDelayMS + gBackPressureDelayReductionThresholdMS))) { int32_t oldBackPressureDelayMS = mBackPressureDelayMS; mBackPressureDelayMS = newBackPressureDelayMS; // If the back pressure delay has gone down we must reset any existing // timers to use the new value. Otherwise we run the risk of executing // timer callbacks out-of-order. ResetTimersForThrottleReduction(oldBackPressureDelayMS); }
--- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -142,17 +142,16 @@ namespace mozilla { namespace layers { class ImageClient; class ImageCompositeNotification; class ImageContainer; class ImageContainerChild; -class PImageContainerChild; class SharedPlanarYCbCrImage; class PlanarYCbCrImage; class TextureClient; class KnowsCompositor; class NVImage; struct ImageBackendData { @@ -578,18 +577,16 @@ public: uint32_t GetDroppedImageCount() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return mDroppedImageCount; } void NotifyComposite(const ImageCompositeNotification& aNotification); - PImageContainerChild* GetPImageContainerChild(); - ImageContainerListener* GetImageContainerListener() { return mNotifyCompositeListener; } /** * Main thread only. */
--- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -27,17 +27,16 @@ #include "nsIWidget.h" // For plugin window configuration information structs namespace mozilla { namespace layers { class ClientPaintedLayer; class CompositorBridgeChild; class ImageLayer; -class PLayerChild; class FrameUniformityData; class ClientLayerManager final : public LayerManager { typedef nsTArray<RefPtr<Layer> > LayerRefArray; public: explicit ClientLayerManager(nsIWidget* aWidget); @@ -375,19 +374,19 @@ public: ToClientLayer(aLayer->GetMaskLayer())->RenderLayer(); } for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) { ToClientLayer(aLayer->GetAncestorMaskLayerAt(i))->RenderLayer(); } } }; -// Create a shadow layer (PLayerChild) for aLayer, if we're forwarding -// our layer tree to a parent process. Record the new layer creation -// in the current open transaction as a side effect. +// Create a LayerHandle for aLayer, if we're forwarding our layer tree +// to a parent process. Record the new layer creation in the current +// open transaction as a side effect. template<typename CreatedMethod> void CreateShadowFor(ClientLayer* aLayer, ClientLayerManager* aMgr, CreatedMethod aMethod) { LayerHandle shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer); if (!shadow) { return;
--- a/gfx/layers/client/CompositableClient.h +++ b/gfx/layers/client/CompositableClient.h @@ -20,17 +20,16 @@ namespace mozilla { namespace layers { class CompositableClient; class ImageBridgeChild; class ImageContainer; class CompositableForwarder; class CompositableChild; -class PCompositableChild; class TextureClientRecycleAllocator; class ContentClientRemote; /** * CompositableClient manages the texture-specific logic for composite layers, * independently of the layer. It is the content side of a CompositableClient/ * CompositableHost pair. *
--- a/gfx/layers/ipc/ShadowLayers.h +++ b/gfx/layers/ipc/ShadowLayers.h @@ -32,17 +32,16 @@ namespace mozilla { namespace layers { class ClientLayerManager; class CompositorBridgeChild; class EditReply; class FixedSizeSmallShmemSectionAllocator; class ImageContainer; class Layer; -class PLayerChild; class PLayerTransactionChild; class LayerTransactionChild; class ShadowableLayer; class SurfaceDescriptor; class TextureClient; class ThebesBuffer; class ThebesBufferData; class Transaction;
--- a/gfx/vr/ipc/VRManagerChild.h +++ b/gfx/vr/ipc/VRManagerChild.h @@ -18,17 +18,16 @@ namespace mozilla { namespace dom { class GamepadManager; class Navigator; class VRDisplay; class VREventObserver; } // namespace dom namespace layers { -class PCompositableChild; class TextureClient; } namespace gfx { class VRLayerChild; class VRDisplayClient; class VRManagerChild : public PVRManagerChild , public layers::TextureForwarder
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -2,16 +2,17 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "vm/TypeInference-inl.h" #include "mozilla/DebugOnly.h" +#include "mozilla/IntegerPrintfMacros.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" #include "mozilla/SizePrintfMacros.h" #include "mozilla/Sprintf.h" #include "jsapi.h" #include "jscntxt.h" #include "jsgc.h" @@ -2601,16 +2602,30 @@ TypeZone::addPendingRecompile(JSContext* // When one script is inlined into another the caller listens to state // changes on the callee's script, so trigger these to force recompilation // of any such callers. if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyGroup()) ObjectStateChange(cx, script->functionNonDelazifying()->group(), false); } +#ifdef JS_CRASH_DIAGNOSTICS +static char sCrashReason[256]; + +MOZ_NORETURN MOZ_COLD MOZ_NEVER_INLINE void +js::ReportMagicWordFailure(uintptr_t actual, uintptr_t expected) +{ + SprintfLiteral(sCrashReason, + "MOZ_CRASH(Got 0x%" PRIxPTR " expected magic word 0x%" PRIxPTR ")", + actual, expected); + MOZ_CRASH_ANNOTATE(sCrashReason); + MOZ_REALLY_CRASH(); +} +#endif + void js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force) { #ifdef DEBUG gc::AutoSuppressGC suppressGC(cx); JSAutoRequest request(cx); Zone* zone = comp->zone();
--- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -538,16 +538,21 @@ class TypeSet #if JS_BITS_PER_WORD == 32 static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4; #else static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4c5c6d7d8; #endif static const uintptr_t TypeConstraintMagic = BaseTypeInferenceMagic + 1; static const uintptr_t ConstraintTypeSetMagic = BaseTypeInferenceMagic + 2; +#ifdef JS_CRASH_DIAGNOSTICS +extern MOZ_NORETURN MOZ_COLD MOZ_NEVER_INLINE void +ReportMagicWordFailure(uintptr_t actual, uintptr_t expected); +#endif + /* * A constraint which listens to additions to a type set and propagates those * changes to other type sets. */ class TypeConstraint { #ifdef JS_CRASH_DIAGNOSTICS uintptr_t magic_; @@ -562,17 +567,18 @@ class TypeConstraint { #ifdef JS_CRASH_DIAGNOSTICS magic_ = TypeConstraintMagic; #endif } void checkMagic() const { #ifdef JS_CRASH_DIAGNOSTICS - MOZ_RELEASE_ASSERT(magic_ == TypeConstraintMagic); + if (MOZ_UNLIKELY(magic_ != TypeConstraintMagic)) + ReportMagicWordFailure(magic_, TypeConstraintMagic); #endif } TypeConstraint* next() const { checkMagic(); if (next_) next_->checkMagic(); return next_; @@ -664,17 +670,18 @@ class ConstraintTypeSet : public TypeSet void initMagic() { MOZ_ASSERT(!magic_); magic_ = ConstraintTypeSetMagic; } #endif void checkMagic() const { #ifdef JS_CRASH_DIAGNOSTICS - MOZ_RELEASE_ASSERT(magic_ == ConstraintTypeSetMagic); + if (MOZ_UNLIKELY(magic_ != ConstraintTypeSetMagic)) + ReportMagicWordFailure(magic_, ConstraintTypeSetMagic); #endif } TypeConstraint* constraintList() const { checkMagic(); if (constraintList_) constraintList_->checkMagic(); return constraintList_;
--- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1712,17 +1712,17 @@ needs-focus != 703186-1.html 703186-2.ht == 713856-dynamic.html 713856-ref.html == 714519-1-as.html 714519-1-ref.html == 714519-1-q.html 714519-1-ref.html == 714519-2-as.html 714519-2-ref.html == 714519-2-q.html 714519-2-ref.html fuzzy-if(true,1,21) fuzzy-if(d2d,68,173) fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html # bug 773482 == 720987.html 720987-ref.html == 722888-1.html 722888-1-ref.html -== 722923-1.html 722923-1-ref.html +fuzzy-if(Android,1,40000) == 722923-1.html 722923-1-ref.html == 723484-1.html 723484-1-ref.html random-if(Android) == 728983-1.html 728983-1-ref.html == 729143-1.html 729143-1-ref.html == 731521-1.html 731521-1-ref.html needs-focus == 731726-1.html 731726-1-ref.html == 735481-1.html 735481-1-ref.html fuzzy-if(cocoaWidget,1,300000) fuzzy-if(skiaContent,2,300000) == 745934-1.html 745934-1-ref.html == 748692-1a.html 748692-1-ref.html
--- a/testing/marionette/client/marionette_driver/marionette.py +++ b/testing/marionette/client/marionette_driver/marionette.py @@ -1,14 +1,13 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import base64 -import copy import datetime import json import os import socket import sys import time import traceback import warnings @@ -540,17 +539,17 @@ class Alert(object): self.marionette._send_message("sendKeysToDialog", body) class Marionette(object): """Represents a Marionette connection to a browser or device.""" CONTEXT_CHROME = "chrome" # non-browser content: windows, dialogs, etc. CONTEXT_CONTENT = "content" # browser content: iframes, divs, etc. - DEFAULT_SOCKET_TIMEOUT = 65 + DEFAULT_SOCKET_TIMEOUT = 60 DEFAULT_STARTUP_TIMEOUT = 120 DEFAULT_SHUTDOWN_TIMEOUT = 65 # Firefox will kill hanging threads after 60s def __init__(self, host="localhost", port=2828, app=None, bin=None, baseurl=None, socket_timeout=DEFAULT_SOCKET_TIMEOUT, startup_timeout=None, **instance_args): """Construct a holder for the Marionette connection. @@ -1243,22 +1242,23 @@ class Marionette(object): ''' Returns an absolute url for files served from Marionette's www directory. :param relative_url: The url of a static file, relative to Marionette's www directory. ''' return "{0}{1}".format(self.baseurl, relative_url) @do_process_check - def start_session(self, capabilities=None, session_id=None, timeout=60): + def start_session(self, desired_capabilities=None, session_id=None, timeout=60): """Create a new Marionette session. This method must be called before performing any other action. - :param capabilities: An optional dict of desired or required capabilities. + :param desired_capabilities: An optional dict of desired + capabilities. This is currently ignored. :param timeout: Timeout in seconds for the server to be ready. :param session_id: unique identifier for the session. If no session id is passed in then one will be generated by the marionette server. :returns: A dict of the capabilities offered. """ self.crashed = 0 @@ -1274,35 +1274,17 @@ class Marionette(object): self.port, self.socket_timeout) # Call wait_for_port() before attempting to connect in # the event gecko hasn't started yet. self.wait_for_port(timeout=timeout) self.protocol, _ = self.client.connect() - if capabilities is not None: - caps = copy.deepcopy(capabilities) - else: - caps = {} - - # Bug 1322277 - Override some default capabilities which are defined by - # the Webdriver spec but which don't really apply to tests executed with - # the Marionette client. Using "desiredCapabilities" here will allow tests - # to override the settings via "desiredCapabilities" and requiredCapabilities". - if "desiredCapabilities" not in caps: - caps.update({ - "desiredCapabilities": { - "timeouts": { - "page load": 60000, # webdriver specifies 300000ms - } - } - }) - - body = {"capabilities": caps, "sessionId": session_id} + body = {"capabilities": desired_capabilities, "sessionId": session_id} resp = self._send_message("newSession", body) self.session_id = resp["sessionId"] self.session = resp["value"] if self.protocol == 1 else resp["capabilities"] # fallback to processId can be removed in Firefox 55 self.process_id = self.session.get("moz:processID", self.session.get("processId")) self.profile = self.session.get("moz:profile")
--- a/testing/marionette/harness/marionette_harness/runner/base.py +++ b/testing/marionette/harness/marionette_harness/runner/base.py @@ -235,17 +235,17 @@ class MarionetteTextTestRunner(Structure def run(self, test): result = super(MarionetteTextTestRunner, self).run(test) result.printLogs(test) return result class BaseMarionetteArguments(ArgumentParser): - socket_timeout_default = 65.0 + socket_timeout_default = 60.0 def __init__(self, **kwargs): ArgumentParser.__init__(self, **kwargs) def dir_path(path): path = os.path.abspath(os.path.expanduser(path)) if not os.access(path, os.F_OK): os.makedirs(path) @@ -342,18 +342,17 @@ class BaseMarionetteArguments(ArgumentPa default=False, help='Enable the jsdebugger for marionette javascript.') self.add_argument('--pydebugger', help='Enable python post-mortem debugger when a test fails.' ' Pass in the debugger you want to use, eg pdb or ipdb.') self.add_argument('--socket-timeout', type=float, default=self.socket_timeout_default, - help='Set the global timeout for marionette socket operations.' - ' Default: %(default)ss.') + help='Set the global timeout for marionette socket operations.') self.add_argument('--disable-e10s', action='store_false', dest='e10s', default=True, help='Disable e10s when running marionette tests.') self.add_argument('--tag', action='append', dest='test_tags', default=None,
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py +++ b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py @@ -6,24 +6,17 @@ from marionette_driver.errors import Ses from marionette_harness import MarionetteTestCase class TestCapabilities(MarionetteTestCase): def setUp(self): super(TestCapabilities, self).setUp() - - # Force default webdriver capabilities by default to test - # Marionette server. - self.marionette.delete_session() - self.marionette.start_session({"desiredCapabilities": {}}) - self.caps = self.marionette.session_capabilities - with self.marionette.using_context("chrome"): self.appinfo = self.marionette.execute_script( "return Services.appinfo") self.os_name = self.marionette.execute_script( "return Services.sysinfo.getProperty('name')").lower() self.os_version = self.marionette.execute_script( "return Services.sysinfo.getProperty('version')") @@ -35,18 +28,20 @@ class TestCapabilities(MarionetteTestCas self.assertIn("acceptInsecureCerts", self.caps) self.assertIn("timeouts", self.caps) self.assertEqual(self.caps["browserName"], self.appinfo["name"].lower()) self.assertEqual(self.caps["browserVersion"], self.appinfo["version"]) self.assertEqual(self.caps["platformName"], self.os_name) self.assertEqual(self.caps["platformVersion"], self.os_version) self.assertFalse(self.caps["acceptInsecureCerts"]) - self.assertEqual(self.caps["timeouts"], - {"implicit": 0, "page load": 300000, "script": 30000}) + self.assertDictEqual(self.caps["timeouts"], + {"implicit": 0, + "page load": 300000, + "script": 30000}) def test_supported_features(self): self.assertIn("rotatable", self.caps) def test_additional_capabilities(self): self.assertIn("moz:processID", self.caps) self.assertEqual(self.caps["moz:processID"], self.appinfo["processID"]) self.assertEqual(self.marionette.process_id, self.appinfo["processID"]) @@ -60,24 +55,16 @@ class TestCapabilities(MarionetteTestCas self.assertEqual(self.caps["moz:profile"], current_profile) self.assertEqual(self.marionette.profile, current_profile) self.assertIn("moz:accessibilityChecks", self.caps) self.assertFalse(self.caps["moz:accessibilityChecks"]) self.assertIn("specificationLevel", self.caps) self.assertEqual(self.caps["specificationLevel"], 0) - def test_default_client_capabilities(self): - self.marionette.delete_session() - self.marionette.start_session() - caps = self.marionette.session_capabilities - - self.assertEqual(caps["timeouts"], - {"implicit": 0, "page load": 60000, "script": 30000}) - def test_set_specification_level(self): self.marionette.delete_session() self.marionette.start_session({"desiredCapabilities": {"specificationLevel": 2}}) caps = self.marionette.session_capabilities self.assertEqual(2, caps["specificationLevel"]) self.marionette.delete_session() self.marionette.start_session({"requiredCapabilities": {"specificationLevel": 3}}) @@ -130,104 +117,97 @@ class TestCapabilityMatching(MarionetteT def test_browser_name_required(self): self.marionette.start_session({"requiredCapabilities": {"browserName": self.browser_name}}) self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name) def test_browser_name_desired_allowed_types(self): for typ in self.allowed: self.marionette.delete_session() self.marionette.start_session({"desiredCapabilities": {"browserName": typ}}) - self.assertEqual(self.marionette.session_capabilities["browserName"], - self.browser_name) + self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name) def test_browser_name_desired_disallowed_types(self): for typ in self.disallowed: with self.assertRaises(SessionNotCreatedException): self.marionette.start_session({"desiredCapabilities": {"browserName": typ}}) def test_browser_name_required_allowed_types(self): for typ in self.allowed: self.marionette.delete_session() self.marionette.start_session({"requiredCapabilities": {"browserName": typ}}) - self.assertEqual(self.marionette.session_capabilities["browserName"], - self.browser_name) + self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name) def test_browser_name_requried_disallowed_types(self): for typ in self.disallowed: with self.assertRaises(SessionNotCreatedException): self.marionette.start_session({"requiredCapabilities": {"browserName": typ}}) def test_browser_name_prefers_required(self): caps = {"desiredCapabilities": {"browserName": "invalid"}, - "requiredCapabilities": {"browserName": "*"}} + "requiredCapabilities": {"browserName": "*"}} self.marionette.start_session(caps) def test_browser_name_error_on_invalid_required(self): with self.assertRaises(SessionNotCreatedException): caps = {"desiredCapabilities": {"browserName": "*"}, - "requiredCapabilities": {"browserName": "invalid"}} + "requiredCapabilities": {"browserName": "invalid"}} self.marionette.start_session(caps) # TODO(ato): browser version comparison not implemented yet def test_platform_name_desired(self): - self.marionette.start_session( - {"desiredCapabilities": {"platformName": self.platform_name}}) + self.marionette.start_session({"desiredCapabilities": {"platformName": self.platform_name}}) self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name) def test_platform_name_required(self): - self.marionette.start_session( - {"requiredCapabilities": {"platformName": self.platform_name}}) + self.marionette.start_session({"requiredCapabilities": {"platformName": self.platform_name}}) self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name) def test_platform_name_desired_allowed_types(self): for typ in self.allowed: self.marionette.delete_session() self.marionette.start_session({"desiredCapabilities": {"platformName": typ}}) - self.assertEqual(self.marionette.session_capabilities["platformName"], - self.platform_name) + self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name) def test_platform_name_desired_disallowed_types(self): for typ in self.disallowed: with self.assertRaises(SessionNotCreatedException): self.marionette.start_session({"desiredCapabilities": {"platformName": typ}}) def test_platform_name_required_allowed_types(self): for typ in self.allowed: self.marionette.delete_session() self.marionette.start_session({"requiredCapabilities": {"platformName": typ}}) - self.assertEqual(self.marionette.session_capabilities["platformName"], - self.platform_name) + self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name) def test_platform_name_requried_disallowed_types(self): for typ in self.disallowed: with self.assertRaises(SessionNotCreatedException): self.marionette.start_session({"requiredCapabilities": {"platformName": typ}}) def test_platform_name_prefers_required(self): caps = {"desiredCapabilities": {"platformName": "invalid"}, - "requiredCapabilities": {"platformName": "*"}} + "requiredCapabilities": {"platformName": "*"}} self.marionette.start_session(caps) def test_platform_name_error_on_invalid_required(self): with self.assertRaises(SessionNotCreatedException): caps = {"desiredCapabilities": {"platformName": "*"}, - "requiredCapabilities": {"platformName": "invalid"}} + "requiredCapabilities": {"platformName": "invalid"}} self.marionette.start_session(caps) # TODO(ato): platform version comparison not imlpemented yet def test_accept_insecure_certs(self): for capability_type in ["desiredCapabilities", "requiredCapabilities"]: print("testing {}".format(capability_type)) for value in ["", 42, {}, []]: print(" type {}".format(type(value))) with self.assertRaises(SessionNotCreatedException): - self.marionette.start_session( - {capability_type: {"acceptInsecureCerts": value}}) + self.marionette.start_session({capability_type: {"acceptInsecureCerts": value}}) self.marionette.delete_session() self.marionette.start_session({"desiredCapabilities": {"acceptInsecureCerts": True}}) self.assertTrue(self.marionette.session_capabilities["acceptInsecureCerts"]) self.marionette.delete_session() self.marionette.start_session({"requiredCapabilities": {"acceptInsecureCerts": True}}) self.assertTrue(self.marionette.session_capabilities["acceptInsecureCerts"]) @@ -256,14 +236,14 @@ class TestCapabilityMatching(MarionetteT def test_proxy_required(self): self.marionette.start_session({"requiredCapabilities": {"proxy": {"proxyType": "manual"}}}) self.assertIn("proxy", self.marionette.session_capabilities) self.assertEqual(self.marionette.session_capabilities["proxy"]["proxyType"], "manual") self.assertEqual(self.marionette.get_pref("network.proxy.type"), 1) def test_timeouts(self): - timeouts = {"implicit": 123, "page load": 456, "script": 789} + timeouts = {u"implicit": 123, u"page load": 456, u"script": 789} caps = {"desiredCapabilities": {"timeouts": timeouts}} self.marionette.start_session(caps) self.assertIn("timeouts", self.marionette.session_capabilities) - self.assertEqual(self.marionette.session_capabilities["timeouts"], timeouts) - self.assertEqual(self.marionette._send_message("getTimeouts"), timeouts) + self.assertDictEqual(self.marionette.session_capabilities["timeouts"], timeouts) + self.assertDictEqual(self.marionette._send_message("getTimeouts"), timeouts)
--- a/testing/web-platform/meta/vr/idlharness.html.ini +++ b/testing/web-platform/meta/vr/idlharness.html.ini @@ -1,10 +1,11 @@ [idlharness.html] type: testharness + prefs: [dom.vr.enabled:true] [Window interface: attribute onvrdisplayblur] expected: FAIL [Window interface: attribute onvrdisplayfocus] expected: FAIL [Navigator interface: attribute vrEnabled] expected: FAIL
--- a/tools/profiler/core/Sampler.cpp +++ b/tools/profiler/core/Sampler.cpp @@ -194,17 +194,17 @@ hasFeature(const char** aFeatures, uint3 for(size_t i = 0; i < aFeatureCount; i++) { if (strcmp(aFeatures[i], aFeature) == 0) return true; } return false; } std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr; -mozilla::UniquePtr<mozilla::Mutex> Sampler::sRegisteredThreadsMutex; +StaticMutex Sampler::sRegisteredThreadsMutex; Sampler::Sampler(double aInterval, int aEntrySize, const char** aFeatures, uint32_t aFeatureCount, const char** aThreadNameFilters, uint32_t aFilterCount) : interval_(aInterval) , paused_(false) , active_(false) , entrySize_(aEntrySize) @@ -246,17 +246,17 @@ Sampler::Sampler(double aInterval, int a for (uint32_t i = 0; i < aFeatureCount; ++i) { mFeatures[i] = aFeatures[i]; } bool ignore; sStartTime = mozilla::TimeStamp::ProcessCreation(ignore); { - MutexAutoLock lock(*sRegisteredThreadsMutex); + StaticMutexAutoLock lock(sRegisteredThreadsMutex); // Set up profiling for each registered thread, if appropriate for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); RegisterThread(info); } } @@ -274,17 +274,17 @@ Sampler::~Sampler() { MOZ_COUNT_DTOR(Sampler); if (IsActive()) Stop(); // Destroy ThreadInfo for all threads { - MutexAutoLock lock(*sRegisteredThreadsMutex); + StaticMutexAutoLock lock(sRegisteredThreadsMutex); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); // We've stopped profiling. We no longer need to retain // information for an old thread. if (info->IsPendingDelete()) { delete info; sRegisteredThreads->erase(sRegisteredThreads->begin() + i); @@ -302,63 +302,63 @@ Sampler::~Sampler() mozilla::tasktracer::StopLogging(); } #endif } void Sampler::Startup() { + StaticMutexAutoLock lock(sRegisteredThreadsMutex); + sRegisteredThreads = new std::vector<ThreadInfo*>(); - sRegisteredThreadsMutex = MakeUnique<Mutex>("sRegisteredThreadsMutex"); // We could create the sLUL object and read unwind info into it at // this point. That would match the lifetime implied by destruction // of it in Sampler::Shutdown just below. However, that gives a big // delay on startup, even if no profiling is actually to be done. // So, instead, sLUL is created on demand at the first call to // Sampler::Start. } void Sampler::Shutdown() { + StaticMutexAutoLock lock(sRegisteredThreadsMutex); + while (sRegisteredThreads->size() > 0) { delete sRegisteredThreads->back(); sRegisteredThreads->pop_back(); } - sRegisteredThreadsMutex = nullptr; - delete sRegisteredThreads; - // UnregisterThread can be called after shutdown in XPCShell. Thus // we need to point to null to ignore such a call after shutdown. - sRegisteredThreadsMutex = nullptr; + delete sRegisteredThreads; sRegisteredThreads = nullptr; #if defined(USE_LUL_STACKWALK) // Delete the sLUL object, if it actually got created. if (sLUL) { delete sLUL; sLUL = nullptr; } #endif } bool Sampler::RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread, void* stackTop) { - if (!sRegisteredThreadsMutex) { + StaticMutexAutoLock lock(sRegisteredThreadsMutex); + + if (!sRegisteredThreads) { return false; } - MutexAutoLock lock(*sRegisteredThreadsMutex); - Thread::tid_t id = Thread::GetCurrentId(); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->ThreadId() == id && !info->IsPendingDelete()) { // Thread already registered. This means the first unregister will be // too early. MOZ_ASSERT(false); @@ -377,22 +377,22 @@ Sampler::RegisterCurrentThread(const cha sRegisteredThreads->push_back(info); return true; } void Sampler::UnregisterCurrentThread() { - if (!sRegisteredThreadsMutex) { + StaticMutexAutoLock lock(sRegisteredThreadsMutex); + + if (!sRegisteredThreads) { return; } - MutexAutoLock lock(*sRegisteredThreadsMutex); - Thread::tid_t id = Thread::GetCurrentId(); for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (info->ThreadId() == id && !info->IsPendingDelete()) { if (profiler_is_active()) { // We still want to show the results of this thread if you // save the profile shortly after a thread is terminated. @@ -421,30 +421,35 @@ Sampler::StreamTaskTracer(SpliceableJSON aWriter.StartArrayProperty("data"); UniquePtr<nsTArray<nsCString>> data = mozilla::tasktracer::GetLoggedData(sStartTime); for (uint32_t i = 0; i < data->Length(); ++i) { aWriter.StringElement((data->ElementAt(i)).get()); } aWriter.EndArray(); aWriter.StartArrayProperty("threads"); - MutexAutoLock lock(*sRegisteredThreadsMutex); + { + StaticMutexAutoLock lock(sRegisteredThreadsMutex); + for (size_t i = 0; i < sRegisteredThreads->size(); i++) { // Thread meta data ThreadInfo* info = sRegisteredThreads->at(i); aWriter.StartObjectElement(); + { if (XRE_GetProcessType() == GeckoProcessType_Plugin) { // TODO Add the proper plugin name aWriter.StringProperty("name", "Plugin"); } else { aWriter.StringProperty("name", info->Name()); } aWriter.IntProperty("tid", static_cast<int>(info->ThreadId())); + } aWriter.EndObject(); } + } aWriter.EndArray(); aWriter.DoubleProperty("start", static_cast<double>(mozilla::tasktracer::GetStartTime())); #endif } void @@ -660,17 +665,17 @@ Sampler::StreamJSON(SpliceableJSONWriter } // Lists the samples for each thread profile aWriter.StartArrayProperty("threads"); { SetPaused(true); { - MutexAutoLock lock(*sRegisteredThreadsMutex); + StaticMutexAutoLock lock(sRegisteredThreadsMutex); for (size_t i = 0; i < sRegisteredThreads->size(); i++) { // Thread not being profiled, skip it ThreadInfo* info = sRegisteredThreads->at(i); if (!info->hasProfile()) { continue; } @@ -716,17 +721,17 @@ Sampler::StreamJSON(SpliceableJSONWriter } void Sampler::FlushOnJSShutdown(JSContext* aContext) { SetPaused(true); { - MutexAutoLock lock(*sRegisteredThreadsMutex); + StaticMutexAutoLock lock(sRegisteredThreadsMutex); for (size_t i = 0; i < sRegisteredThreads->size(); i++) { // Thread not being profiled, skip it. ThreadInfo* info = sRegisteredThreads->at(i); if (!info->hasProfile() || info->IsPendingDelete()) { continue; }
--- a/tools/profiler/core/platform-linux.cc +++ b/tools/profiler/core/platform-linux.cc @@ -156,29 +156,16 @@ static void paf_parent(void) { // Set up the fork handlers. static void* setup_atfork() { pthread_atfork(paf_prepare, paf_parent, NULL); return NULL; } #endif /* !defined(ANDROID) */ -struct SamplerRegistry { - static void AddActiveSampler(Sampler *sampler) { - MOZ_ASSERT(!SamplerRegistry::sampler); - SamplerRegistry::sampler = sampler; - } - static void RemoveActiveSampler(Sampler *sampler) { - SamplerRegistry::sampler = NULL; - } - static Sampler *sampler; -}; - -Sampler *SamplerRegistry::sampler = NULL; - static mozilla::Atomic<ThreadInfo*> sCurrentThreadInfo; static sem_t sSignalHandlingDone; static void SetSampleContext(TickSample* sample, void* context) { // Extracting the sample from the context is extremely machine dependent. ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); mcontext_t& mcontext = ucontext->uc_mcontext; @@ -299,28 +286,26 @@ static void* SignalSender(void* arg) { int vm_tgid_ = getpid(); DebugOnly<int> my_tid = gettid(); unsigned int nSignalsSent = 0; TimeDuration lastSleepOverhead = 0; TimeStamp sampleStart = TimeStamp::Now(); - while (SamplerRegistry::sampler->IsActive()) { - - SamplerRegistry::sampler->DeleteExpiredMarkers(); + // XXX: this loop is an off-main-thread use of gSampler + while (gSampler->IsActive()) { + gSampler->DeleteExpiredMarkers(); - if (!SamplerRegistry::sampler->IsPaused()) { - MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); - const std::vector<ThreadInfo*>& threads = - SamplerRegistry::sampler->GetRegisteredThreads(); + if (!gSampler->IsPaused()) { + StaticMutexAutoLock lock(Sampler::sRegisteredThreadsMutex); bool isFirstProfiledThread = true; - for (uint32_t i = 0; i < threads.size(); i++) { - ThreadInfo* info = threads[i]; + for (uint32_t i = 0; i < Sampler::sRegisteredThreads->size(); i++) { + ThreadInfo* info = (*Sampler::sRegisteredThreads)[i]; // This will be null if we're not interested in profiling this thread. if (!info->hasProfile() || info->IsPendingDelete()) { continue; } PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); if (sleeping == PseudoStack::SLEEPING_AGAIN) { @@ -366,17 +351,18 @@ static void* SignalSender(void* arg) { if ((++nSignalsSent & 0xF) == 0) { # if defined(USE_LUL_STACKWALK) sLUL->MaybeShowStats(); # endif } } } - TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(SamplerRegistry::sampler->interval() * 1000); + TimeStamp targetSleepEndTime = + sampleStart + TimeDuration::FromMicroseconds(gSampler->interval() * 1000); TimeStamp beforeSleep = TimeStamp::Now(); TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep; double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds()); OS::SleepMicro(sleepTime); sampleStart = TimeStamp::Now(); lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime)); } return 0; @@ -390,18 +376,16 @@ void Sampler::Start() { #elif defined(USE_LUL_STACKWALK) // NOTE: this isn't thread-safe. But we expect Sampler::Start to be // called only from the main thread, so this is OK in general. if (!sLUL) { sLUL_initialization_routine(); } #endif - SamplerRegistry::AddActiveSampler(this); - // Initialize signal handler communication sCurrentThreadInfo = nullptr; if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) { LOG("Error initializing semaphore"); return; } // Request profiling signals. @@ -447,18 +431,16 @@ void Sampler::Stop() { // Wait for signal sender termination (it will exit after setting // active_ to false). if (signal_sender_launched_) { pthread_join(signal_sender_thread_, NULL); signal_sender_launched_ = false; } - SamplerRegistry::RemoveActiveSampler(this); - // Restore old signal handler if (signal_handler_installed_) { sigaction(SIGPROF, &old_sigprof_signal_handler_, 0); signal_handler_installed_ = false; } } #ifdef ANDROID
--- a/tools/profiler/core/platform-macos.cc +++ b/tools/profiler/core/platform-macos.cc @@ -38,31 +38,16 @@ #include "platform.h" #include "mozilla/TimeStamp.h" using mozilla::TimeStamp; using mozilla::TimeDuration; // this port is based off of v8 svn revision 9837 -// XXX: this is a very stubbed out implementation -// that only supports a single Sampler -struct SamplerRegistry { - static void AddActiveSampler(Sampler *sampler) { - MOZ_ASSERT(!SamplerRegistry::sampler); - SamplerRegistry::sampler = sampler; - } - static void RemoveActiveSampler(Sampler *sampler) { - SamplerRegistry::sampler = NULL; - } - static Sampler *sampler; -}; - -Sampler *SamplerRegistry::sampler = NULL; - #ifdef DEBUG // 0 is never a valid thread id on MacOSX since a pthread_t is a pointer. static const pthread_t kNoThread = (pthread_t) 0; #endif void OS::Startup() { } @@ -148,76 +133,75 @@ public: pthread_create(&mThread, attr_ptr, ThreadEntry, this); MOZ_ASSERT(mThread != kNoThread); } void Join() { pthread_join(mThread, NULL); } - static void AddActiveSampler(Sampler* sampler) { - SamplerRegistry::AddActiveSampler(sampler); + static void AddActiveSampler() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mInstance == NULL) { - mInstance = new SamplerThread(sampler->interval()); + mInstance = new SamplerThread(gSampler->interval()); mInstance->Start(); } } - static void RemoveActiveSampler(Sampler* sampler) { + static void RemoveActiveSampler() { mInstance->Join(); - //XXX: unlike v8 we need to remove the active sampler after doing the Join - // because we drop the sampler immediately - SamplerRegistry::RemoveActiveSampler(sampler); delete mInstance; mInstance = NULL; } void Run() { TimeDuration lastSleepOverhead = 0; TimeStamp sampleStart = TimeStamp::Now(); - while (SamplerRegistry::sampler->IsActive()) { - SamplerRegistry::sampler->DeleteExpiredMarkers(); - if (!SamplerRegistry::sampler->IsPaused()) { - MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); - const std::vector<ThreadInfo*>& threads = - SamplerRegistry::sampler->GetRegisteredThreads(); + + // XXX: this loop is an off-main-thread use of gSampler + while (gSampler->IsActive()) { + gSampler->DeleteExpiredMarkers(); + + if (!gSampler->IsPaused()) { + StaticMutexAutoLock lock(Sampler::sRegisteredThreadsMutex); + bool isFirstProfiledThread = true; - for (uint32_t i = 0; i < threads.size(); i++) { - ThreadInfo* info = threads[i]; + for (uint32_t i = 0; i < Sampler::sRegisteredThreads->size(); i++) { + ThreadInfo* info = (*Sampler::sRegisteredThreads)[i]; // This will be null if we're not interested in profiling this thread. if (!info->hasProfile() || info->IsPendingDelete()) { continue; } PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); if (sleeping == PseudoStack::SLEEPING_AGAIN) { info->DuplicateLastSample(); continue; } info->UpdateThreadResponsiveness(); - SampleContext(SamplerRegistry::sampler, info, isFirstProfiledThread); + SampleContext(info, isFirstProfiledThread); isFirstProfiledThread = false; } } TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(mIntervalMicro); TimeStamp beforeSleep = TimeStamp::Now(); TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep; double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds()); OS::SleepMicro(sleepTime); sampleStart = TimeStamp::Now(); lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime)); } } - void SampleContext(Sampler* sampler, ThreadInfo* aThreadInfo, - bool isFirstProfiledThread) + void SampleContext(ThreadInfo* aThreadInfo, bool isFirstProfiledThread) { thread_act_t profiled_thread = aThreadInfo->GetPlatformData()->profiled_thread(); TickSample sample_obj; TickSample* sample = &sample_obj; // Unique Set Size is not supported on Mac. @@ -262,17 +246,18 @@ public: reinterpret_cast<natural_t*>(&state), &count) == KERN_SUCCESS) { sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip)); sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp)); sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp)); sample->timestamp = mozilla::TimeStamp::Now(); sample->threadInfo = aThreadInfo; - sampler->Tick(sample); + // XXX: this is an off-main-thread use of gSampler + gSampler->Tick(sample); } thread_resume(profiled_thread); } private: pthread_t mThread; int mIntervalMicro; @@ -284,23 +269,23 @@ private: #undef REGISTER_FIELD SamplerThread* SamplerThread::mInstance = NULL; void Sampler::Start() { MOZ_ASSERT(!IsActive()); SetActive(true); - SamplerThread::AddActiveSampler(this); + SamplerThread::AddActiveSampler(); } void Sampler::Stop() { MOZ_ASSERT(IsActive()); SetActive(false); - SamplerThread::RemoveActiveSampler(this); + SamplerThread::RemoveActiveSampler(); } /* static */ Thread::tid_t Thread::GetCurrentId() { return gettid(); }
--- a/tools/profiler/core/platform-win32.cc +++ b/tools/profiler/core/platform-win32.cc @@ -91,20 +91,19 @@ static const HANDLE kNoThread = INVALID_ // Start() method is called the new thread starts running the Run() method in // the new thread. The SamplerThread object should not be deallocated before // the thread has terminated. class SamplerThread { public: // Initialize a Win32 thread object. The thread has an invalid thread // handle until it is started. - SamplerThread(double interval, Sampler* sampler) + explicit SamplerThread(double interval) : mStackSize(0) , mThread(kNoThread) - , mSampler(sampler) , mInterval(interval) { mInterval = floor(interval + 0.5); if (mInterval <= 0) { mInterval = 1; } } @@ -135,76 +134,77 @@ class SamplerThread } void Join() { if (mThreadId != Thread::GetCurrentId()) { WaitForSingleObject(mThread, INFINITE); } } - static void StartSampler(Sampler* sampler) { + static void StartSampler() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mInstance == NULL) { - mInstance = new SamplerThread(sampler->interval(), sampler); + mInstance = new SamplerThread(gSampler->interval()); mInstance->Start(); } else { - MOZ_ASSERT(mInstance->mInterval == sampler->interval()); + MOZ_ASSERT(mInstance->mInterval == gSampler->interval()); } } static void StopSampler() { mInstance->Join(); delete mInstance; mInstance = NULL; } void Run() { // By default we'll not adjust the timer resolution which tends to be around // 16ms. However, if the requested interval is sufficiently low we'll try to // adjust the resolution to match. if (mInterval < 10) ::timeBeginPeriod(mInterval); - while (mSampler->IsActive()) { - mSampler->DeleteExpiredMarkers(); + // XXX: this loop is an off-main-thread use of gSampler + while (gSampler->IsActive()) { + gSampler->DeleteExpiredMarkers(); - if (!mSampler->IsPaused()) { - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); - const std::vector<ThreadInfo*>& threads = - mSampler->GetRegisteredThreads(); + if (!gSampler->IsPaused()) { + mozilla::StaticMutexAutoLock lock(Sampler::sRegisteredThreadsMutex); + bool isFirstProfiledThread = true; - for (uint32_t i = 0; i < threads.size(); i++) { - ThreadInfo* info = threads[i]; + for (uint32_t i = 0; i < Sampler::sRegisteredThreads->size(); i++) { + ThreadInfo* info = (*Sampler::sRegisteredThreads)[i]; // This will be null if we're not interested in profiling this thread. if (!info->hasProfile() || info->IsPendingDelete()) { continue; } PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); if (sleeping == PseudoStack::SLEEPING_AGAIN) { info->DuplicateLastSample(); continue; } info->UpdateThreadResponsiveness(); - SampleContext(mSampler, info, isFirstProfiledThread); + SampleContext(info, isFirstProfiledThread); isFirstProfiledThread = false; } } OS::Sleep(mInterval); } // disable any timer resolution changes we've made if (mInterval < 10) ::timeEndPeriod(mInterval); } - void SampleContext(Sampler* sampler, ThreadInfo* aThreadInfo, - bool isFirstProfiledThread) + void SampleContext(ThreadInfo* aThreadInfo, bool isFirstProfiledThread) { uintptr_t thread = Sampler::GetThreadHandle(aThreadInfo->GetPlatformData()); HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread); if (profiled_thread == NULL) return; // Context used for sampling the register state of the profiled thread. CONTEXT context; @@ -276,41 +276,42 @@ class SamplerThread sample->fp = reinterpret_cast<Address>(context.Rbp); #else sample->pc = reinterpret_cast<Address>(context.Eip); sample->sp = reinterpret_cast<Address>(context.Esp); sample->fp = reinterpret_cast<Address>(context.Ebp); #endif sample->context = &context; - sampler->Tick(sample); + + // XXX: this is an off-main-thread use of gSampler + gSampler->Tick(sample); ResumeThread(profiled_thread); } private: int mStackSize; HANDLE mThread; Thread::tid_t mThreadId; - Sampler* mSampler; int mInterval; // units: ms // Protects the process wide state below. static SamplerThread* mInstance; DISALLOW_COPY_AND_ASSIGN(SamplerThread); }; SamplerThread* SamplerThread::mInstance = NULL; void Sampler::Start() { MOZ_ASSERT(!IsActive()); SetActive(true); - SamplerThread::StartSampler(this); + SamplerThread::StartSampler(); } void Sampler::Stop() { MOZ_ASSERT(IsActive()); SetActive(false); SamplerThread::StopSampler(); }
--- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -739,21 +739,20 @@ profiler_start(int aProfileEntries, doub gSampler = new Sampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL, aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount); gSampler->Start(); if (gSampler->ProfileJS() || gSampler->InPrivacyMode()) { - mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); - const std::vector<ThreadInfo*>& threads = gSampler->GetRegisteredThreads(); + mozilla::StaticMutexAutoLock lock(Sampler::sRegisteredThreadsMutex); - for (uint32_t i = 0; i < threads.size(); i++) { - ThreadInfo* info = threads[i]; + for (uint32_t i = 0; i < Sampler::sRegisteredThreads->size(); i++) { + ThreadInfo* info = (*Sampler::sRegisteredThreads)[i]; if (info->IsPendingDelete() || !info->hasProfile()) { continue; } info->Stack()->reinitializeOnResume(); if (gSampler->ProfileJS()) { info->Stack()->enableJSSampling(); } if (gSampler->InPrivacyMode()) {
--- a/tools/profiler/core/platform.h +++ b/tools/profiler/core/platform.h @@ -37,17 +37,17 @@ #ifdef XP_UNIX #include <pthread.h> #endif #include <stdint.h> #include <math.h> #include "MainThreadUtils.h" -#include "mozilla/Mutex.h" +#include "mozilla/StaticMutex.h" #include "ThreadResponsiveness.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" #include "mozilla/Unused.h" #include "mozilla/Vector.h" #include "PlatformMacros.h" #include "v8-support.h" #include <vector> @@ -292,30 +292,27 @@ public: // If we move the backtracing code into the platform files we won't // need to have these hacks #ifdef XP_WIN // xxxehsan sucky hack :( static uintptr_t GetThreadHandle(PlatformData*); #endif - static const std::vector<ThreadInfo*>& GetRegisteredThreads() { - return *sRegisteredThreads; - } - static bool RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack, bool aIsMainThread, void* stackTop); static void UnregisterCurrentThread(); static void Startup(); // Should only be called on shutdown static void Shutdown(); - static mozilla::UniquePtr<mozilla::Mutex> sRegisteredThreadsMutex; + static mozilla::StaticMutex sRegisteredThreadsMutex; + static std::vector<ThreadInfo*>* sRegisteredThreads; static bool CanNotifyObservers() { #ifdef MOZ_WIDGET_GONK // We use profile.sh on b2g to manually select threads and options per process. return false; #elif defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) // Android ANR reporter uses the profiler off the main thread return NS_IsMainThread(); @@ -360,18 +357,16 @@ private: void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime); // Called within a signal. This function must be reentrant void InplaceTick(TickSample* sample); void SetActive(bool value) { NoBarrier_Store(&active_, value); } - static std::vector<ThreadInfo*>* sRegisteredThreads; - const double interval_; Atomic32 paused_; Atomic32 active_; const int entrySize_; // Refactor me! #if defined(SPS_OS_linux) || defined(SPS_OS_android) bool signal_handler_installed_;