From 1ab0ff809cd40394f42997b33aa6c7b99b920bc6 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 14 Jan 2015 20:28:27 -0800 Subject: [PATCH 001/133] Bug 1121864 - Fix some minor nits in NetworkGeolocationProvider.js. r=garvank --- dom/system/NetworkGeolocationProvider.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dom/system/NetworkGeolocationProvider.js b/dom/system/NetworkGeolocationProvider.js index 67cc415097ce..190b9fb41c03 100755 --- a/dom/system/NetworkGeolocationProvider.js +++ b/dom/system/NetworkGeolocationProvider.js @@ -2,6 +2,8 @@ * 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/. */ +"use strict"; + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); @@ -318,13 +320,12 @@ WifiGeoPositionProvider.prototype = { } }; - try { - Services.obs.addObserver(this, SETTINGS_CHANGED_TOPIC, false); - let settings = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService); + Services.obs.addObserver(this, SETTINGS_CHANGED_TOPIC, false); + let settingsService = Cc["@mozilla.org/settingsService;1"]; + if (settingsService) { + let settings = settingsService.getService(Ci.nsISettingsService); settings.createLock().get(SETTINGS_WIFI_ENABLED, settingsCallback); settings.createLock().get(SETTINGS_DEBUG_ENABLED, settingsCallback); - } catch(ex) { - // This platform doesn't have the settings interface, and that is just peachy } if (gWifiScanningEnabled && Cc["@mozilla.org/wifi/monitor;1"]) { @@ -509,7 +510,7 @@ WifiGeoPositionProvider.prototype = { [POSITION_UNAVAILABLE]); }).bind(this); xhr.onload = (function() { - LOG("gls returned status: " + xhr.status + " --> " + JSON.stringify(xhr.response)); + LOG("server returned status: " + xhr.status + " --> " + JSON.stringify(xhr.response)); if ((xhr.channel instanceof Ci.nsIHttpChannel && xhr.status != 200) || !xhr.response || !xhr.response.location) { this.notifyListener("notifyError", From e7cc95bb4726550d37a01987421bcbe809ece4dd Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Fri, 16 Jan 2015 09:42:42 +0000 Subject: [PATCH 002/133] Bug 1122020 - Account for position offset when collecting glyph bounding boxes. r=roc --- gfx/thebes/gfxFont.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 61feab91b439..5a677efc022f 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2269,7 +2269,7 @@ gfxFont::Measure(gfxTextRun *aTextRun, if (isRTL) { glyphRect -= gfxPoint(advance, 0); } - glyphRect += gfxPoint(x, 0); + glyphRect += glyphPt; metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect); x += direction*advance; } From 15ab02733194a1ac95a21264cebc888de953479e Mon Sep 17 00:00:00 2001 From: Kai Engert Date: Fri, 16 Jan 2015 11:40:18 +0100 Subject: [PATCH 003/133] Bug 1107731 - Upgrade Mozilla 37 to use NSS 3.18. Landing BETA6. r=wtc --- security/nss/TAG-INFO | 2 +- security/nss/cmd/pp/pp.c | 3 +- security/nss/coreconf/coreconf.dep | 1 - security/nss/doc/Makefile | 13 ---- .../nss/external_tests/ssl_gtest/ssl_gtest.cc | 9 ++- .../ssl_gtest/ssl_loopback_unittest.cc | 77 ++++++++++++++++++- security/nss/lib/certdb/certt.h | 4 +- security/nss/lib/freebl/ecl/README | 39 +--------- security/nss/lib/freebl/mpi/README | 39 +--------- security/nss/lib/freebl/mpi/utils/README | 39 +--------- .../nss/lib/libpkix/include/pkix_revchecker.h | 4 +- .../pkix/checker/pkix_revocationchecker.c | 10 ++- .../pkix/checker/pkix_revocationmethod.h | 5 +- .../nss/lib/libpkix/pkix/top/pkix_build.c | 19 +++-- security/nss/lib/pk11wrap/pk11cert.c | 6 +- security/nss/lib/pk11wrap/pk11mech.c | 7 +- security/nss/lib/softoken/fipstokn.c | 11 ++- security/nss/lib/ssl/ssl3con.c | 10 +-- security/nss/lib/ssl/ssl3ext.c | 11 +-- .../nss/pkg/solaris/common_files/copyright | 38 +-------- security/nss/tests/libpkix/sample_apps/README | 39 +--------- 21 files changed, 149 insertions(+), 237 deletions(-) diff --git a/security/nss/TAG-INFO b/security/nss/TAG-INFO index 3254890c95e5..dde7cbc3cb13 100644 --- a/security/nss/TAG-INFO +++ b/security/nss/TAG-INFO @@ -1 +1 @@ -NSS_3_18_BETA5 +NSS_3_18_BETA6 diff --git a/security/nss/cmd/pp/pp.c b/security/nss/cmd/pp/pp.c index a739a915ecdb..31e766112afb 100644 --- a/security/nss/cmd/pp/pp.c +++ b/security/nss/cmd/pp/pp.c @@ -31,8 +31,7 @@ static void Usage(char *progName) SEC_CT_CERTIFICATE, SEC_CT_CERTIFICATE_REQUEST); fprintf(stderr, "%-14s %s (ci), %s (p7), %s or %s (n).\n", "", SEC_CT_CERTIFICATE_ID, SEC_CT_PKCS7, SEC_CT_CRL, SEC_CT_NAME); - fprintf(stderr, "%-14s (Use either the long type name or the shortcut.)\n", "", SEC_CT_CERTIFICATE_ID, - SEC_CT_PKCS7, SEC_CT_CRL, SEC_CT_NAME); + fprintf(stderr, "%-14s (Use either the long type name or the shortcut.)\n", ""); fprintf(stderr, "%-14s Input is in ascii encoded form (RFC1113)\n", "-a"); fprintf(stderr, "%-14s Define an input file to use (default is stdin)\n", diff --git a/security/nss/coreconf/coreconf.dep b/security/nss/coreconf/coreconf.dep index 590d1bfaeee3..5182f75552c8 100644 --- a/security/nss/coreconf/coreconf.dep +++ b/security/nss/coreconf/coreconf.dep @@ -10,4 +10,3 @@ */ #error "Do not include this header file." - diff --git a/security/nss/doc/Makefile b/security/nss/doc/Makefile index 00c94aaf803f..444a81a30f7d 100644 --- a/security/nss/doc/Makefile +++ b/security/nss/doc/Makefile @@ -39,19 +39,6 @@ version.xml: .PHONY : $(HTMLPAGES) .PHONY : $(TXTPAGES) -#------------------------------------------ -# Package a tar ball for building in fedora -# Include the makefile and .xml files only -# man pages will be created at build time -#------------------------------------------ - -tarball: - rm -rf $(name); \ - mkdir -p $(name)/nroff; \ - cp Makefile $(name); \ - cp *.xml $(name); \ - tar cvjf $(name)-$(date).tar.bz2 $(name) - #-------------------------------------------------------- # manpages #-------------------------------------------------------- diff --git a/security/nss/external_tests/ssl_gtest/ssl_gtest.cc b/security/nss/external_tests/ssl_gtest/ssl_gtest.cc index 0b57f941d053..3173890c172d 100644 --- a/security/nss/external_tests/ssl_gtest/ssl_gtest.cc +++ b/security/nss/external_tests/ssl_gtest/ssl_gtest.cc @@ -7,21 +7,22 @@ #define GTEST_HAS_RTTI 0 #include "gtest/gtest.h" +std::string g_working_dir_path; + int main(int argc, char **argv) { // Start the tests ::testing::InitGoogleTest(&argc, argv); - std::string path = "."; + g_working_dir_path = "."; for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], "-d")) { - path = argv[i + 1]; + g_working_dir_path = argv[i + 1]; ++i; } } - NSS_Initialize(path.c_str(), "", "", SECMOD_DB, NSS_INIT_READONLY); + NSS_Initialize(g_working_dir_path.c_str(), "", "", SECMOD_DB, NSS_INIT_READONLY); NSS_SetDomesticPolicy(); - int rv = RUN_ALL_TESTS(); NSS_Shutdown(); diff --git a/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc b/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc index cb355e155c23..3e95e7d909fb 100644 --- a/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc +++ b/security/nss/external_tests/ssl_gtest/ssl_loopback_unittest.cc @@ -16,6 +16,8 @@ #include "gtest/gtest.h" #include "gtest_utils.h" +extern std::string g_working_dir_path; + namespace nss_test { #define LOG(a) std::cerr << name_ << ": " << a << std::endl; @@ -211,7 +213,10 @@ class TlsAgent : public PollTarget { adapter_(nullptr), ssl_fd_(nullptr), role_(role), - state_(INIT) {} + state_(INIT) { + memset(&info_, 0, sizeof(info_)); + memset(&csinfo_, 0, sizeof(csinfo_)); + } ~TlsAgent() { if (pr_fd_) { @@ -287,6 +292,10 @@ class TlsAgent : public PollTarget { SECKEY_DestroyPrivateKey(priv); CERT_DestroyCertificate(cert); + } else { + SECStatus rv = SSL_SetURL(ssl_fd_, "server"); + EXPECT_EQ(SECSuccess, rv); + if (rv != SECSuccess) return false; } SECStatus rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook, @@ -297,6 +306,22 @@ class TlsAgent : public PollTarget { return true; } + void SetSessionTicketsEnabled(bool en) { + ASSERT_TRUE(EnsureTlsSetup()); + + SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS, + en ? PR_TRUE : PR_FALSE); + ASSERT_EQ(SECSuccess, rv); + } + + void SetSessionCacheEnabled(bool en) { + ASSERT_TRUE(EnsureTlsSetup()); + + SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_NO_CACHE, + en ? PR_FALSE : PR_TRUE); + ASSERT_EQ(SECSuccess, rv); + } + void SetVersionRange(uint16_t minver, uint16_t maxver) { SSLVersionRange range = {minver, maxver}; ASSERT_EQ(SECSuccess, SSL_VersionRangeSet(ssl_fd_, &range)); @@ -374,6 +399,11 @@ class TlsAgent : public PollTarget { } } + std::vector GetSessionId() { + return std::vector(info_.sessionID, + info_.sessionID + info_.sessionIDLength); + } + private: const static char* states[]; @@ -426,7 +456,19 @@ class TlsConnectTestBase : public ::testing::Test { delete server_; } - void SetUp() { Init(); } + void SetUp() { + // Configure a fresh session cache. + SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str()); + + Init(); + } + + void TearDown() { + client_ = nullptr; + server_ = nullptr; + + SSL_ShutdownServerSessionIDCache(); + } void Init() { ASSERT_TRUE(client_->Init()); @@ -471,6 +513,14 @@ class TlsConnectTestBase : public ::testing::Test { std::cerr << "Connected with cipher suite " << client_->cipher_suite_name() << std::endl; + + // Check and store session ids. + std::vector sid_c1 = client_->GetSessionId(); + ASSERT_EQ(32, sid_c1.size()); + std::vector sid_s1 = server_->GetSessionId(); + ASSERT_EQ(32, sid_s1.size()); + ASSERT_EQ(sid_c1, sid_s1); + session_id_ = sid_c1; } void EnableSomeECDHECiphers() { @@ -482,6 +532,7 @@ class TlsConnectTestBase : public ::testing::Test { Mode mode_; TlsAgent* client_; TlsAgent* server_; + std::vector session_id_; }; class TlsConnectTest : public TlsConnectTestBase { @@ -516,6 +567,26 @@ TEST_P(TlsConnectGeneric, Connect) { } } +TEST_P(TlsConnectGeneric, ConnectResumed) { + Connect(); + std::vector old_sid = session_id_; + + Reset(); + Connect(); + ASSERT_EQ(old_sid, session_id_) << "Session was not resumed when it should have been"; +} + +TEST_P(TlsConnectGeneric, ConnectNotResumed) { + Connect(); + std::vector old_sid = session_id_; + + Reset(); + client_->SetSessionCacheEnabled(false); + Connect(); + + ASSERT_NE(old_sid, session_id_) << "Session was resumed when it should not have been"; +} + TEST_P(TlsConnectGeneric, ConnectTLS_1_1_Only) { EnsureTlsSetup(); client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, @@ -561,6 +632,7 @@ TEST_F(TlsConnectTest, ConnectECDHETwiceReuseKey) { new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); server_->SetInspector(i2); EnableSomeECDHECiphers(); + client_->SetSessionCacheEnabled(false); Connect(); client_->CheckKEAType(ssl_kea_ecdh); @@ -594,6 +666,7 @@ TEST_F(TlsConnectTest, ConnectECDHETwiceNewKey) { TlsInspectorRecordHandshakeMessage* i2 = new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); server_->SetInspector(i2); + client_->SetSessionCacheEnabled(false); Connect(); client_->CheckKEAType(ssl_kea_ecdh); diff --git a/security/nss/lib/certdb/certt.h b/security/nss/lib/certdb/certt.h index 9ab00fde16fa..4f3c91166abb 100644 --- a/security/nss/lib/certdb/certt.h +++ b/security/nss/lib/certdb/certt.h @@ -1177,7 +1177,7 @@ typedef struct { /* * How many preferred methods are specified? * This is equivalent to the size of the array that - * preferred_revocation_methods points to. + * preferred_methods points to. * It's allowed to set this value to zero, * then NSS will decide which methods to prefer. */ @@ -1186,7 +1186,7 @@ typedef struct { /* Array that may specify an optional order of preferred methods. * Each array entry shall contain a method identifier as defined * by CERTRevocationMethodIndex. - * The entry at index [0] specifies the method with highest preferrence. + * The entry at index [0] specifies the method with highest preference. * These methods will be tested first for locally available information. * Methods allowed for downloading will be attempted in the same order. */ diff --git a/security/nss/lib/freebl/ecl/README b/security/nss/lib/freebl/ecl/README index b4c92400dee8..f086cdefa0a1 100644 --- a/security/nss/lib/freebl/ecl/README +++ b/security/nss/lib/freebl/ecl/README @@ -1,39 +1,6 @@ -***** BEGIN LICENSE BLOCK ***** -Version: MPL 1.1/GPL 2.0/LGPL 2.1 - -The contents of this file are subject to the Mozilla Public License Version -1.1 (the "License"); you may not use this file except in compliance with -the License. You may obtain a copy of the License at -http://www.mozilla.org/MPL/ - -Software distributed under the License is distributed on an "AS IS" basis, -WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -for the specific language governing rights and limitations under the -License. - -The Original Code is the elliptic curve math library. - -The Initial Developer of the Original Code is Sun Microsystems, Inc. -Portions created by Sun Microsystems, Inc. are Copyright (C) 2003 -Sun Microsystems, Inc. All Rights Reserved. - -Contributor(s): - Stephen Fung and - Douglas Stebila , Sun Microsystems Laboratories - -Alternatively, the contents of this file may be used under the terms of -either the GNU General Public License Version 2 or later (the "GPL"), or -the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -in which case the provisions of the GPL or the LGPL are applicable instead -of those above. If you wish to allow use of your version of this file only -under the terms of either the GPL or the LGPL, and not to allow others to -use your version of this file under the terms of the MPL, indicate your -decision by deleting the provisions above and replace them with the notice -and other provisions required by the GPL or the LGPL. If you do not delete -the provisions above, a recipient may use your version of this file under -the terms of any one of the MPL, the GPL or the LGPL. - -***** END LICENSE BLOCK ***** +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/. The ECL exposes routines for constructing and converting curve parameters for internal use. diff --git a/security/nss/lib/freebl/mpi/README b/security/nss/lib/freebl/mpi/README index 156356bcc2dc..fc6c5e101206 100644 --- a/security/nss/lib/freebl/mpi/README +++ b/security/nss/lib/freebl/mpi/README @@ -1,39 +1,6 @@ -***** BEGIN LICENSE BLOCK ***** -Version: MPL 1.1/GPL 2.0/LGPL 2.1 - -The contents of this file are subject to the Mozilla Public License Version -1.1 (the "License"); you may not use this file except in compliance with -the License. You may obtain a copy of the License at -http://www.mozilla.org/MPL/ - -Software distributed under the License is distributed on an "AS IS" basis, -WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -for the specific language governing rights and limitations under the -License. - -The Original Code is the MPI Arbitrary Precision Integer Arithmetic -library. - -The Initial Developer of the Original Code is -Michael J. Fromberger -Portions created by the Initial Developer are Copyright (C) 1997-2000 -the Initial Developer. All Rights Reserved. - -Contributor(s): - -Alternatively, the contents of this file may be used under the terms of -either the GNU General Public License Version 2 or later (the "GPL"), or -the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -in which case the provisions of the GPL or the LGPL are applicable instead -of those above. If you wish to allow use of your version of this file only -under the terms of either the GPL or the LGPL, and not to allow others to -use your version of this file under the terms of the MPL, indicate your -decision by deleting the provisions above and replace them with the notice -and other provisions required by the GPL or the LGPL. If you do not delete -the provisions above, a recipient may use your version of this file under -the terms of any one of the MPL, the GPL or the LGPL. - -***** END LICENSE BLOCK ***** +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/. About the MPI Library --------------------- diff --git a/security/nss/lib/freebl/mpi/utils/README b/security/nss/lib/freebl/mpi/utils/README index f2e926c27832..61c8e2efa507 100644 --- a/security/nss/lib/freebl/mpi/utils/README +++ b/security/nss/lib/freebl/mpi/utils/README @@ -1,39 +1,6 @@ -***** BEGIN LICENSE BLOCK ***** -Version: MPL 1.1/GPL 2.0/LGPL 2.1 - -The contents of this file are subject to the Mozilla Public License Version -1.1 (the "License"); you may not use this file except in compliance with -the License. You may obtain a copy of the License at -http://www.mozilla.org/MPL/ - -Software distributed under the License is distributed on an "AS IS" basis, -WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -for the specific language governing rights and limitations under the -License. - -The Original Code is the MPI Arbitrary Precision Integer Arithmetic -library. - -The Initial Developer of the Original Code is -Michael J. Fromberger -Portions created by the Initial Developer are Copyright (C) 1998, 2000 -the Initial Developer. All Rights Reserved. - -Contributor(s): - -Alternatively, the contents of this file may be used under the terms of -either the GNU General Public License Version 2 or later (the "GPL"), or -the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -in which case the provisions of the GPL or the LGPL are applicable instead -of those above. If you wish to allow use of your version of this file only -under the terms of either the GPL or the LGPL, and not to allow others to -use your version of this file under the terms of the MPL, indicate your -decision by deleting the provisions above and replace them with the notice -and other provisions required by the GPL or the LGPL. If you do not delete -the provisions above, a recipient may use your version of this file under -the terms of any one of the MPL, the GPL or the LGPL. - -***** END LICENSE BLOCK ***** +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/. Additional MPI utilities ------------------------ diff --git a/security/nss/lib/libpkix/include/pkix_revchecker.h b/security/nss/lib/libpkix/include/pkix_revchecker.h index 9f65a8444631..18a10cd2369d 100755 --- a/security/nss/lib/libpkix/include/pkix_revchecker.h +++ b/security/nss/lib/libpkix/include/pkix_revchecker.h @@ -117,7 +117,7 @@ PKIX_RevocationChecker_Create( * "methodFlags" * Set of flags for the method. * "methodPriority" - * Method priority. (0 corresponds to a highest priority) + * Method priority. (0 corresponds to the highest priority) * "verificationFn" * User call back function that will perform validation of fetched * revocation information(new crl or ocsp response) @@ -143,7 +143,7 @@ PKIX_RevocationChecker_CreateAndAddMethod( PKIX_ProcessingParams *params, PKIX_RevocationMethodType methodType, PKIX_UInt32 methodFlags, - PKIX_UInt32 mathodPriority, + PKIX_UInt32 methodPriority, PKIX_PL_VerifyCallback verificationFn, PKIX_Boolean isLeafMethod, void *plContext); diff --git a/security/nss/lib/libpkix/pkix/checker/pkix_revocationchecker.c b/security/nss/lib/libpkix/pkix/checker/pkix_revocationchecker.c index d1499a7dc5f4..ebe37739fa5a 100755 --- a/security/nss/lib/libpkix/pkix/checker/pkix_revocationchecker.c +++ b/security/nss/lib/libpkix/pkix/checker/pkix_revocationchecker.c @@ -137,7 +137,7 @@ pkix_RevocationChecker_RegisterSelf(void *plContext) PKIX_RETURN(REVOCATIONCHECKER); } -/* Sort methods by theirs priorities */ +/* Sort methods by their priorities (lower priority = higher preference) */ static PKIX_Error * pkix_RevocationChecker_SortComparator( PKIX_PL_Object *obj1, @@ -152,7 +152,13 @@ pkix_RevocationChecker_SortComparator( method1 = (pkix_RevocationMethod *)obj1; method2 = (pkix_RevocationMethod *)obj2; - *pResult = (method1->priority > method2->priority); + if (method1->priority < method2->priority) { + *pResult = -1; + } else if (method1->priority > method2->priority) { + *pResult = 1; + } else { + *pResult = 0; + } PKIX_RETURN(BUILD); } diff --git a/security/nss/lib/libpkix/pkix/checker/pkix_revocationmethod.h b/security/nss/lib/libpkix/pkix/checker/pkix_revocationmethod.h index 32e452556f83..193223731ba9 100644 --- a/security/nss/lib/libpkix/pkix/checker/pkix_revocationmethod.h +++ b/security/nss/lib/libpkix/pkix/checker/pkix_revocationmethod.h @@ -48,8 +48,9 @@ pkix_ExternalRevocationCheckFn(PKIX_PL_Cert *cert, PKIX_PL_Cert *issuer, void **pNBIOContext, void *plContext); /* Revocation method structure assosiates revocation types with - * a set of flags on the method, a priority of the method, and - * method local/external checker functions. */ + * a set of flags on the method, a priority of the method (0 + * corresponds to the highest priority), and method local/external + * checker functions. */ struct pkix_RevocationMethodStruct { PKIX_RevocationMethodType methodType; PKIX_UInt32 flags; diff --git a/security/nss/lib/libpkix/pkix/top/pkix_build.c b/security/nss/lib/libpkix/pkix/top/pkix_build.c index 0fd4fadca2e8..9ca307e43fbf 100755 --- a/security/nss/lib/libpkix/pkix/top/pkix_build.c +++ b/security/nss/lib/libpkix/pkix/top/pkix_build.c @@ -660,9 +660,11 @@ pkix_ForwardBuilderState_IsIOPending( * DESCRIPTION: * * This Function takes two Certificates cast in "obj1" and "obj2", - * compares their validity NotAfter dates and returns the result at - * "pResult". The comparison key(s) can be expanded by using other - * data in the Certificate in the future. + * compares them to determine which is a more preferable certificate + * for chain building. This Function is suitable for use as a + * comparator callback for pkix_List_BubbleSort, setting "*pResult" to + * > 0 if "obj1" is less desirable than "obj2" and < 0 if "obj1" + * is more desirable than "obj2". * * PARAMETERS: * "obj1" @@ -691,14 +693,14 @@ pkix_Build_SortCertComparator( { PKIX_PL_Date *date1 = NULL; PKIX_PL_Date *date2 = NULL; - PKIX_Boolean result = PKIX_FALSE; + PKIX_Int32 result = 0; PKIX_ENTER(BUILD, "pkix_Build_SortCertComparator"); PKIX_NULLCHECK_THREE(obj1, obj2, pResult); /* * For sorting candidate certificates, we use NotAfter date as the - * sorted key for now (can be expanded if desired in the future). + * comparison key for now (can be expanded if desired in the future). * * In PKIX_BuildChain, the List of CertStores was reordered so that * trusted CertStores are ahead of untrusted CertStores. That sort, or @@ -727,7 +729,12 @@ pkix_Build_SortCertComparator( plContext), PKIX_OBJECTCOMPARATORFAILED); - *pResult = !result; + /* + * Invert the result, so that if date1 is greater than date2, + * obj1 is sorted before obj2. This is because pkix_List_BubbleSort + * sorts in ascending order. + */ + *pResult = -result; cleanup: diff --git a/security/nss/lib/pk11wrap/pk11cert.c b/security/nss/lib/pk11wrap/pk11cert.c index 29addd8be4c6..3e6a839f3a8e 100644 --- a/security/nss/lib/pk11wrap/pk11cert.c +++ b/security/nss/lib/pk11wrap/pk11cert.c @@ -293,13 +293,11 @@ PK11_MakeCertFromHandle(PK11SlotInfo *slot,CK_OBJECT_HANDLE certID, char * nickname = NULL; CERTCertificate *cert = NULL; CERTCertTrust *trust; - PRBool isFortezzaRootCA = PR_FALSE; - PRBool swapNickname = PR_FALSE; cert = pk11_fastCert(slot,certID,privateLabel, &nickname); if (cert == NULL) goto loser; - + if (nickname) { if (cert->nickname != NULL) { cert->dbnickname = cert->nickname; @@ -307,7 +305,6 @@ PK11_MakeCertFromHandle(PK11SlotInfo *slot,CK_OBJECT_HANDLE certID, cert->nickname = PORT_ArenaStrdup(cert->arena,nickname); PORT_Free(nickname); nickname = NULL; - swapNickname = PR_TRUE; } /* remember where this cert came from.... If we have just looked @@ -343,7 +340,6 @@ PK11_MakeCertFromHandle(PK11SlotInfo *slot,CK_OBJECT_HANDLE certID, * full trust on explicitly */ if (PK11_DoesMechanism(slot,CKM_KEA_KEY_DERIVE)) { trust->objectSigningFlags |= CERTDB_VALID_CA; - isFortezzaRootCA = PR_TRUE; } } if ((type & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) { diff --git a/security/nss/lib/pk11wrap/pk11mech.c b/security/nss/lib/pk11wrap/pk11mech.c index e15a286b1d31..b7a7296b39a9 100644 --- a/security/nss/lib/pk11wrap/pk11mech.c +++ b/security/nss/lib/pk11wrap/pk11mech.c @@ -1378,12 +1378,13 @@ pk11_GenerateNewParamWithKeyLen(CK_MECHANISM_TYPE type, int keyLen) SECItem iv; SECStatus rv; - mech = (SECItem *) PORT_Alloc(sizeof(SECItem)); if (mech == NULL) return NULL; rv = SECSuccess; mech->type = siBuffer; + mech->data = NULL; + mech->len = 0; switch (type) { case CKM_RC4: case CKM_SEED_ECB: @@ -1396,8 +1397,6 @@ pk11_GenerateNewParamWithKeyLen(CK_MECHANISM_TYPE type, int keyLen) case CKM_CAST_ECB: case CKM_CAST3_ECB: case CKM_CAST5_ECB: - mech->data = NULL; - mech->len = 0; break; case CKM_RC2_ECB: rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS)); @@ -1445,8 +1444,6 @@ pk11_GenerateNewParamWithKeyLen(CK_MECHANISM_TYPE type, int keyLen) return PK11_ParamFromIV(type,&iv); default: if (pk11_lookup(type)->iv == 0) { - mech->data = NULL; - mech->len = 0; break; } case CKM_SEED_CBC: diff --git a/security/nss/lib/softoken/fipstokn.c b/security/nss/lib/softoken/fipstokn.c index 9435e71c661c..3cb6b794def3 100644 --- a/security/nss/lib/softoken/fipstokn.c +++ b/security/nss/lib/softoken/fipstokn.c @@ -720,13 +720,22 @@ CK_RV FC_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { CK_OBJECT_CLASS * classptr; + CK_RV rv = CKR_OK; - SFTK_FIPSCHECK(); CHECK_FORK(); classptr = (CK_OBJECT_CLASS *)fc_getAttribute(pTemplate,ulCount,CKA_CLASS); if (classptr == NULL) return CKR_TEMPLATE_INCOMPLETE; + if (*classptr == CKO_NETSCAPE_NEWSLOT || *classptr == CKO_NETSCAPE_DELSLOT) { + if (sftk_fatalError) + return CKR_DEVICE_ERROR; + } else { + rv = sftk_fipsCheck(); + if (rv != CKR_OK) + return rv; + } + /* FIPS can't create keys from raw key material */ if (SFTK_IS_NONPUBLIC_KEY_OBJECT(*classptr)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c index 761e4360e581..529eb42b504b 100644 --- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -8744,11 +8744,11 @@ ssl3_PickSignatureHashAlgorithm(sslSocket *ss, unsigned int i, j; /* hashPreference expresses our preferences for hash algorithms, most * preferable first. */ - static const PRUint8 hashPreference[] = { - tls_hash_sha256, - tls_hash_sha384, - tls_hash_sha512, - tls_hash_sha1, + static const SECOidTag hashPreference[] = { + SEC_OID_SHA256, + SEC_OID_SHA384, + SEC_OID_SHA512, + SEC_OID_SHA1, }; switch (ss->ssl3.hs.kea_def->kea) { diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c index 247f1f8f162b..0bcaa04239b3 100644 --- a/security/nss/lib/ssl/ssl3ext.c +++ b/security/nss/lib/ssl/ssl3ext.c @@ -2258,7 +2258,7 @@ ssl3_ServerHandleSigAlgsXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data) SECStatus rv; SECItem algorithms; const unsigned char *b; - unsigned int numAlgorithms, i; + unsigned int numAlgorithms, i, j; /* Ignore this extension if we aren't doing TLS 1.2 or greater. */ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_2) { @@ -2294,7 +2294,7 @@ ssl3_ServerHandleSigAlgsXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data) ss->ssl3.hs.numClientSigAndHash = 0; b = algorithms.data; - for (i = 0; i < numAlgorithms; i++) { + for (i = j = 0; i < numAlgorithms; i++) { unsigned char tls_hash = *(b++); unsigned char tls_sig = *(b++); SECOidTag hash = ssl3_TLSHashAlgorithmToOID(tls_hash); @@ -2305,9 +2305,10 @@ ssl3_ServerHandleSigAlgsXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data) } /* tls_sig support will be checked later in * ssl3_PickSignatureHashAlgorithm. */ - ss->ssl3.hs.clientSigAndHash[i].hashAlg = hash; - ss->ssl3.hs.clientSigAndHash[i].sigAlg = tls_sig; - ss->ssl3.hs.numClientSigAndHash++; + ss->ssl3.hs.clientSigAndHash[j].hashAlg = hash; + ss->ssl3.hs.clientSigAndHash[j].sigAlg = tls_sig; + ++j; + ++ss->ssl3.hs.numClientSigAndHash; } if (!ss->ssl3.hs.numClientSigAndHash) { diff --git a/security/nss/pkg/solaris/common_files/copyright b/security/nss/pkg/solaris/common_files/copyright index 988939bb1820..c5534908d38e 100644 --- a/security/nss/pkg/solaris/common_files/copyright +++ b/security/nss/pkg/solaris/common_files/copyright @@ -1,38 +1,6 @@ Copyright 2005 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. -***** BEGIN LICENSE BLOCK ***** -Version: MPL 1.1/GPL 2.0/LGPL 2.1 - -The contents of this package are subject to the Mozilla Public License Version -1.1 (the "License"); you may not use this package except in compliance with -the License. You may obtain a copy of the License at -http://www.mozilla.org/MPL/ - -Software distributed under the License is distributed on an "AS IS" basis, -WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -for the specific language governing rights and limitations under the -License. - -The Original Code is the Netscape Portable Runtime (NSPR). - -The Initial Developer of the Original Code is -Netscape Communications Corporation. -Portions created by the Initial Developer are Copyright (C) 1998-2000 -the Initial Developer. All Rights Reserved. - -Contributor(s): - -Alternatively, the contents of this file may be used under the terms of -either the GNU General Public License Version 2 or later (the "GPL"), or -the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -in which case the provisions of the GPL or the LGPL are applicable instead -of those above. If you wish to allow use of your version of this file only -under the terms of either the GPL or the LGPL, and not to allow others to -use your version of this file under the terms of the MPL, indicate your -decision by deleting the provisions above and replace them with the notice -and other provisions required by the GPL or the LGPL. If you do not delete -the provisions above, a recipient may use your version of this file under -the terms of any one of the MPL, the GPL or the LGPL. - -***** END LICENSE BLOCK ***** +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/. diff --git a/security/nss/tests/libpkix/sample_apps/README b/security/nss/tests/libpkix/sample_apps/README index 266b2ba53738..012e7bf7ebf6 100755 --- a/security/nss/tests/libpkix/sample_apps/README +++ b/security/nss/tests/libpkix/sample_apps/README @@ -1,39 +1,6 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is the PKIX-C library. -# -# The Initial Developer of the Original Code is -# Sun Microsystems, Inc. -# Portions created by the Initial Developer are -# Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. -# -# Contributor(s): -# Sun Microsystems, Inc. -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** +# 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/. This directory contains both sample applications and performance evaluation applications. From 5dcd3154ee2fd35a9283a2a5ebf5bebc820a1b36 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Fri, 16 Jan 2015 21:15:06 +0900 Subject: [PATCH 004/133] Bug 1121878 Map Copy, Cut, ChannelDown, ChannelUp and MediaFastForward to proper WM_APPCOMMAND message r=smaug+jimm --- widget/NativeKeyToDOMKeyName.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/widget/NativeKeyToDOMKeyName.h b/widget/NativeKeyToDOMKeyName.h index fd0797d6d8c0..930447d1b211 100644 --- a/widget/NativeKeyToDOMKeyName.h +++ b/widget/NativeKeyToDOMKeyName.h @@ -298,6 +298,7 @@ KEY_MAP_QT (Clear, Qt::Key_Clear) KEY_MAP_ANDROID (Clear, AKEYCODE_CLEAR) // Copy +KEY_MAP_WIN_CMD (Copy, APPCOMMAND_COPY) KEY_MAP_GTK (Copy, GDK_Copy) KEY_MAP_QT (Copy, Qt::Key_Copy) @@ -306,6 +307,7 @@ KEY_MAP_WIN (CrSel, VK_CRSEL) KEY_MAP_GTK (CrSel, GDK_3270_CursorSelect) // legacy IBM keyboard layout // Cut +KEY_MAP_WIN_CMD (Cut, APPCOMMAND_CUT) KEY_MAP_GTK (Cut, GDK_Cut) KEY_MAP_QT (Cut, Qt::Key_Cut) @@ -1185,9 +1187,11 @@ KEY_MAP_ANDROID (AVRInput, AKEYCODE_AVR_INPUT) KEY_MAP_ANDROID (AVRPower, AKEYCODE_AVR_POWER) // ChannelDown +KEY_MAP_WIN_CMD (ChannelDown, APPCOMMAND_MEDIA_CHANNEL_DOWN) KEY_MAP_ANDROID (ChannelDown, AKEYCODE_CHANNEL_DOWN) // ChannelUp +KEY_MAP_WIN_CMD (ChannelUp, APPCOMMAND_MEDIA_CHANNEL_UP) KEY_MAP_ANDROID (ChannelUp, AKEYCODE_CHANNEL_UP) // ColorF0Red @@ -1221,6 +1225,7 @@ KEY_MAP_ANDROID (Guide, AKEYCODE_GUIDE) KEY_MAP_ANDROID (Info, AKEYCODE_INFO) // MediaFastForward +KEY_MAP_WIN_CMD (MediaFastForward, APPCOMMAND_MEDIA_FAST_FORWARD) KEY_MAP_GTK (MediaFastForward, GDK_AudioForward) KEY_MAP_QT (MediaFastForward, Qt::Key_AudioForward) KEY_MAP_ANDROID (MediaFastForward, AKEYCODE_MEDIA_FAST_FORWARD) From acd33f956ce6ca3b38faa7780ea696419faa6bb1 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Thu, 15 Jan 2015 13:20:05 -0500 Subject: [PATCH 005/133] Bug 1122143 - use gmtime_s on windows in ComputeUTCTime; r=till ComputeLocalTime uses localtime_s; we should use the equivalent in ComputeUTCTime. --- js/src/vm/DateTime.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/src/vm/DateTime.cpp b/js/src/vm/DateTime.cpp index fd0290148042..8c97b4c216c8 100644 --- a/js/src/vm/DateTime.cpp +++ b/js/src/vm/DateTime.cpp @@ -31,7 +31,9 @@ ComputeLocalTime(time_t local, struct tm *ptm) static bool ComputeUTCTime(time_t t, struct tm *ptm) { -#ifdef HAVE_GMTIME_R +#if defined(_WIN32) + return gmtime_s(ptm, &t) == 0; +#elif defined(HAVE_GMTIME_R) return gmtime_r(&t, ptm); #else struct tm *otm = gmtime(&t); From 0b9afb21b86ed450fd6fd96f2711cfaa69b01d49 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Fri, 16 Jan 2015 21:48:38 +0900 Subject: [PATCH 006/133] Bug 1120393 - Serialize/deserialize nsITransportSecurity.errorCode. r=keeler --- .../manager/ssl/src/TransportSecurityInfo.cpp | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/security/manager/ssl/src/TransportSecurityInfo.cpp b/security/manager/ssl/src/TransportSecurityInfo.cpp index 8a5ccbdf8d93..b8eb146809b8 100644 --- a/security/manager/ssl/src/TransportSecurityInfo.cpp +++ b/security/manager/ssl/src/TransportSecurityInfo.cpp @@ -22,6 +22,7 @@ #include "nsComponentManagerUtils.h" #include "nsReadableUtils.h" #include "nsServiceManagerUtils.h" +#include "nsXULAppAPI.h" #include "PSMRunnable.h" #include "secerr.h" @@ -234,11 +235,15 @@ TransportSecurityInfo::formatErrorMessage(MutexAutoLock const & proofOfLock, bool wantsHtml, bool suppressPort443, nsString &result) { + result.Truncate(); if (errorCode == 0) { - result.Truncate(); return NS_OK; } + if (XRE_GetProcessType() != GeckoProcessType_Default) { + return NS_ERROR_UNEXPECTED; + } + nsresult rv; NS_ConvertASCIItoUTF16 hostNameU(mHostName); NS_ASSERTION(errorMessageType != OverridableCertErrorMessage || @@ -296,8 +301,8 @@ TransportSecurityInfo::GetInterface(const nsIID & uuid, void * *result) // of the previous value. This is so when older versions attempt to // read a newer serialized TransportSecurityInfo, they will actually // fail and return NS_ERROR_FAILURE instead of silently failing. -#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x328f, 0x45ab, \ - { 0xa8, 0xa4, 0x35, 0x18, 0x80, 0x04, 0x77, 0x8d } } +#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x1faa, 0x4169, \ + { 0xb0, 0xd2, 0x81, 0x29, 0xec, 0x7c, 0xb1, 0xde } } static NS_DEFINE_CID(kTransportSecurityInfoMagic, TRANSPORTSECURITYINFOMAGIC); NS_IMETHODIMP @@ -322,12 +327,18 @@ TransportSecurityInfo::Write(nsIObjectOutputStream* stream) if (NS_FAILED(rv)) { return rv; } - // XXX: uses nsNSSComponent string bundles off the main thread - rv = formatErrorMessage(lock, mErrorCode, mErrorMessageType, true, true, - mErrorMessageCached); + rv = stream->Write32(static_cast(mErrorCode)); if (NS_FAILED(rv)) { return rv; } + if (mErrorMessageCached.IsEmpty()) { + // XXX: uses nsNSSComponent string bundles off the main thread + rv = formatErrorMessage(lock, mErrorCode, mErrorMessageType, + true, true, mErrorMessageCached); + if (NS_FAILED(rv)) { + return rv; + } + } rv = stream->WriteWStringZ(mErrorMessageCached.get()); if (NS_FAILED(rv)) { return rv; @@ -394,13 +405,19 @@ TransportSecurityInfo::Read(nsIObjectInputStream* stream) return NS_ERROR_UNEXPECTED; } mSubRequestsNoSecurity = subRequestsNoSecurity; + uint32_t errorCode; + rv = stream->Read32(&errorCode); + if (NS_FAILED(rv)) { + return rv; + } + // PRErrorCode will be a negative value + mErrorCode = static_cast(errorCode); + rv = stream->ReadString(mErrorMessageCached); if (NS_FAILED(rv)) { return rv; } - mErrorCode = 0; - // For successful connections and for connections with overridable errors, // mSSLStatus will be non-null. For connections with non-overridable errors, // it will be null. From cb76e55fd8c8506c7557c97d690259348030c9e4 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Fri, 16 Jan 2015 21:48:38 +0900 Subject: [PATCH 007/133] Bug 1120393 - unittest to ensure nsITransportSecurityInfo.errorCode is correctly serialized. r=keeler --- .../manager/ssl/tests/unit/test_cert_chains.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/security/manager/ssl/tests/unit/test_cert_chains.js b/security/manager/ssl/tests/unit/test_cert_chains.js index 6c08f42c2af4..21017182473d 100644 --- a/security/manager/ssl/tests/unit/test_cert_chains.js +++ b/security/manager/ssl/tests/unit/test_cert_chains.js @@ -40,6 +40,21 @@ function test_cert_list_serialization() { do_check_true(certList.equals(deserialized)); } +function test_security_info_serialization(securityInfo, expectedErrorCode) { + // Serialize the securityInfo to a string + let serHelper = Cc["@mozilla.org/network/serialization-helper;1"] + .getService(Ci.nsISerializationHelper); + let serialized = serHelper.serializeToString(securityInfo); + + // Deserialize from the string and compare to the original object + let deserialized = serHelper.deserializeObject(serialized); + deserialized.QueryInterface(Ci.nsITransportSecurityInfo); + do_check_eq(securityInfo.securityState, deserialized.securityState); + do_check_eq(securityInfo.errorMessage, deserialized.errorMessage); + do_check_eq(securityInfo.errorCode, expectedErrorCode); + do_check_eq(deserialized.errorCode, expectedErrorCode); +} + function run_test() { do_get_profile(); add_tls_server_setup("BadCertServer"); @@ -62,6 +77,7 @@ function run_test() { "good.include-subdomains.pinning.example.com", Cr.NS_OK, null, function withSecurityInfo(aTransportSecurityInfo) { aTransportSecurityInfo.QueryInterface(Ci.nsITransportSecurityInfo); + test_security_info_serialization(aTransportSecurityInfo, 0); do_check_eq(aTransportSecurityInfo.failedCertChain, null); } ); @@ -73,6 +89,7 @@ function run_test() { null, function withSecurityInfo(securityInfo) { securityInfo.QueryInterface(Ci.nsITransportSecurityInfo); + test_security_info_serialization(securityInfo, SEC_ERROR_EXPIRED_CERTIFICATE); do_check_neq(securityInfo.failedCertChain, null); let originalCertChain = build_cert_chain(["expired-ee", "test-ca"]); do_check_true(originalCertChain.equals(securityInfo.failedCertChain)); @@ -86,6 +103,7 @@ function run_test() { null, function withSecurityInfo(securityInfo) { securityInfo.QueryInterface(Ci.nsITransportSecurityInfo); + test_security_info_serialization(securityInfo, SEC_ERROR_INADEQUATE_KEY_USAGE); do_check_neq(securityInfo.failedCertChain, null); let originalCertChain = build_cert_chain(["inadequatekeyusage-ee", "test-ca"]); do_check_true(originalCertChain.equals(securityInfo.failedCertChain)); From 66c53a18383ddda70fd79096c3751ec5834003f7 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Fri, 16 Jan 2015 21:48:38 +0900 Subject: [PATCH 008/133] Bug 1120393 - Mochitest for SSL error reporting. r=felipc --- browser/base/content/test/general/pinning_reports.sjs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/browser/base/content/test/general/pinning_reports.sjs b/browser/base/content/test/general/pinning_reports.sjs index 16cf98d08f04..fde7d16ae4d0 100644 --- a/browser/base/content/test/general/pinning_reports.sjs +++ b/browser/base/content/test/general/pinning_reports.sjs @@ -3,6 +3,8 @@ const EXPECTED_CHAIN = [ "MIIC2jCCAcKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDExtBbHRlcm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkwHhcNMTQwOTI1MjEyMTU0WhcNMjQwOTI1MjEyMTU0WjAmMSQwIgYDVQQDExtBbHRlcm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBT+BwAhO52IWgSIdZZifU9LHOs3IR/+8DCC0WP5d/OuyKlZ6Rqd0tsd3i7durhQyjHSbLf2lJStcnFjcVEbEnNI76RuvlN8xLLn5eV+2Ayr4cZYKztudwRmw+DV/iYAiMSy0hs7m3ssfX7qpoi1aNRjUanwU0VTCPQhF1bEKAC2du+C5Z8e92zN5t87w7bYr7lt+m8197XliXEu+0s9RgnGwGaZ296BIRz6NOoJYTa43n06LU1I1+Z4d6lPdzUFrSR0GBaMhUSurUBtOin3yWiMhg1VHX/KwqGc4als5GyCVXy8HGrA/0zQPOhetxrlhEVAdK/xBt7CZvByj1Rcc7AgMBAAGjEzARMA8GA1UdEwQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAJq/hogSRqzPWTwX4wTn/DVSNdWwFLv53qep9YrSMJ8ZsfbfK9Es4VP4dBLRQAVMJ0Z5mW1I6d/n0KayTanuUBvemYdxPi/qQNSs8UJcllqdhqWzmzAg6a0LxrMnEeKzPBPD6q8PwQ7tYP+B4sBN9tnnsnyPgti9ZiNZn5FwXZliHXseQ7FE9/SqHlLw5LXW3YtKjuti6RmuV6fq3j+D4oeC5vb1mKgIyoTqGN6ze57v8RHi+pQ8Q+kmoUn/L3Z2YmFe4SKN/4WoyXr8TdejpThGOCGCAd3565s5gOx5QfSQX11P8NZKO8hcN0tme3VzmGpHK0Z/6MTmdpNaTwQ6odk=" ]; +const MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = -16384; + function parseReport(request) { // read the report from the request let inputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream); @@ -38,6 +40,12 @@ function handleRequest(request, response) { } } + if (report.errorCode !== MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) { + response.setStatusLine("1.1", 500, "Server error"); + response.write("The report contained an unexpected error code"); + return; + } + // if all is as expected, send the 201 the client expects response.setStatusLine("1.1", 201, "Created"); response.write("OK"); From bf9881e5b7a7e0ce9b7921e0de9f2bf847e8a48c Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:48:24 +1100 Subject: [PATCH 009/133] Bug 1119119: Do not abort when calling appendBuffer with no data. r=cajbir --HG-- extra : rebase_source : 4836370af1e1558771700331d40d2312cd225201 --- dom/media/mediasource/SourceBuffer.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp index 10aa327c2317..999eaba1e86c 100644 --- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -347,19 +347,21 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments, "We don't handle timestampOffset for sequence mode yet"); - if (!mTrackBuffer->AppendData(aData, aLength, mTimestampOffset * USECS_PER_S)) { - Optional decodeError(MediaSourceEndOfStreamError::Decode); - ErrorResult dummy; - mMediaSource->EndOfStream(decodeError, dummy); - aRv.Throw(NS_ERROR_FAILURE); - return; - } + if (aLength) { + if (!mTrackBuffer->AppendData(aData, aLength, mTimestampOffset * USECS_PER_S)) { + Optional decodeError(MediaSourceEndOfStreamError::Decode); + ErrorResult dummy; + mMediaSource->EndOfStream(decodeError, dummy); + aRv.Throw(NS_ERROR_FAILURE); + return; + } - if (mTrackBuffer->HasInitSegment()) { - mMediaSource->QueueInitializationEvent(); - } + if (mTrackBuffer->HasInitSegment()) { + mMediaSource->QueueInitializationEvent(); + } - CheckEndTime(); + CheckEndTime(); + } // Run the final step of the buffer append algorithm asynchronously to // ensure the SourceBuffer's updating flag transition behaves as required From 962cc5167e52ab673b2095a5eccf58dfbd09283d Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:48:25 +1100 Subject: [PATCH 010/133] Bug 1119119: Update web-platform-tests expected data. r=karlt --HG-- extra : rebase_source : 6b70000ea202a366169c6e97cb80d49cbd609627 --- .../meta/media-source/interfaces.html.ini | 70 ++++++++++++++++++- .../mediasource-append-buffer.html.ini | 6 -- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/testing/web-platform/meta/media-source/interfaces.html.ini b/testing/web-platform/meta/media-source/interfaces.html.ini index 507b00d92de6..210320c4f13a 100644 --- a/testing/web-platform/meta/media-source/interfaces.html.ini +++ b/testing/web-platform/meta/media-source/interfaces.html.ini @@ -1,3 +1,71 @@ [interfaces.html] type: testharness - expected: ERROR + [AudioTrack interface: attribute kind] + expected: FAIL + + [AudioTrack interface: attribute language] + expected: FAIL + + [AudioTrack interface: attribute sourceBuffer] + expected: FAIL + + [VideoTrack interface: attribute kind] + expected: FAIL + + [VideoTrack interface: attribute language] + expected: FAIL + + [VideoTrack interface: attribute sourceBuffer] + expected: FAIL + + [TextTrack interface: attribute kind] + expected: FAIL + + [TextTrack interface: attribute language] + expected: FAIL + + [TextTrack interface: attribute sourceBuffer] + expected: FAIL + + [MediaSource interface: existence and properties of interface object] + expected: FAIL + + [SourceBuffer interface: existence and properties of interface object] + expected: FAIL + + [SourceBuffer interface: attribute audioTracks] + expected: FAIL + + [SourceBuffer interface: attribute videoTracks] + expected: FAIL + + [SourceBuffer interface: attribute textTracks] + expected: FAIL + + [SourceBuffer interface: operation appendStream(Stream,unsigned long long)] + expected: FAIL + + [SourceBuffer interface: sourceBuffer must inherit property "audioTracks" with the proper type (4)] + expected: FAIL + + [SourceBuffer interface: sourceBuffer must inherit property "videoTracks" with the proper type (5)] + expected: FAIL + + [SourceBuffer interface: sourceBuffer must inherit property "textTracks" with the proper type (6)] + expected: FAIL + + [SourceBuffer interface: sourceBuffer must inherit property "appendStream" with the proper type (11)] + expected: FAIL + + [SourceBuffer interface: calling appendStream(Stream,unsigned long long) on sourceBuffer with too few arguments must throw TypeError] + expected: FAIL + + [SourceBufferList interface: existence and properties of interface object] + expected: FAIL + + [VideoPlaybackQuality interface: attribute totalFrameDelay] + expected: FAIL + + [VideoPlaybackQuality interface: video.getVideoPlaybackQuality() must inherit property "totalFrameDelay" with the proper type (4)] + expected: FAIL + diff --git a/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini b/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini index ff8bcebdb507..e9593a2f65e0 100644 --- a/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini @@ -3,9 +3,3 @@ [Test MediaSource.removeSourceBuffer() call during a pending appendBuffer().] expected: FAIL - [Test appending an empty ArrayBufferView.] - expected: FAIL - - [Test appending an empty ArrayBuffer.] - expected: FAIL - From 510550aff7524da2622a6a76539c555a9401d742 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:48:25 +1100 Subject: [PATCH 011/133] Bug 1120084: Implement MSE's AppendErrorAlgorithm --HG-- extra : rebase_source : c9e1db9f0dd5cb504328f7572364227a4e40a937 --- dom/media/mediasource/SourceBuffer.cpp | 29 ++++++++++++++++++++++---- dom/media/mediasource/SourceBuffer.h | 6 ++++++ dom/media/mediasource/TrackBuffer.cpp | 6 ++++++ dom/media/mediasource/TrackBuffer.h | 5 +++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp index 999eaba1e86c..83d32e79d87b 100644 --- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -349,10 +349,8 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR "We don't handle timestampOffset for sequence mode yet"); if (aLength) { if (!mTrackBuffer->AppendData(aData, aLength, mTimestampOffset * USECS_PER_S)) { - Optional decodeError(MediaSourceEndOfStreamError::Decode); - ErrorResult dummy; - mMediaSource->EndOfStream(decodeError, dummy); - aRv.Throw(NS_ERROR_FAILURE); + nsCOMPtr event = NS_NewRunnableMethodWithArg(this, &SourceBuffer::AppendError, true); + NS_DispatchToMainThread(event); return; } @@ -370,6 +368,29 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR NS_DispatchToMainThread(event); } +void +SourceBuffer::AppendError(bool aDecoderError) +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!mUpdating) { + // The buffer append algorithm has been interrupted by abort(). + return; + } + mTrackBuffer->ResetParserState(); + + mUpdating = false; + + QueueAsyncSimpleEvent("error"); + QueueAsyncSimpleEvent("updateend"); + + if (aDecoderError) { + Optional decodeError( + MediaSourceEndOfStreamError::Decode); + ErrorResult dummy; + mMediaSource->EndOfStream(decodeError, dummy); + } +} + bool SourceBuffer::PrepareAppend(ErrorResult& aRv) { diff --git a/dom/media/mediasource/SourceBuffer.h b/dom/media/mediasource/SourceBuffer.h index a12375a4eea9..204bf39afa8c 100644 --- a/dom/media/mediasource/SourceBuffer.h +++ b/dom/media/mediasource/SourceBuffer.h @@ -135,6 +135,12 @@ private: // Shared implementation of AppendBuffer overloads. void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv); + // Implement the "Append Error Algorithm". + // Will call endOfStream() with "decode" error if aDecodeError is true. + // 3.5.3 Append Error Algorithm + // http://w3c.github.io/media-source/#sourcebuffer-append-error + void AppendError(bool aDecoderError); + // Implements the "Prepare Append Algorithm". Returns true if the append // may continue, or false (with aRv set) on error. bool PrepareAppend(ErrorResult& aRv); diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index 56a8b5e610ac..b943b2d6e873 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -611,6 +611,12 @@ TrackBuffer::ResetDecode() } } +void +TrackBuffer::ResetParserState() +{ + // TODO +} + const nsTArray>& TrackBuffer::Decoders() { diff --git a/dom/media/mediasource/TrackBuffer.h b/dom/media/mediasource/TrackBuffer.h index ea3a18b2480d..8b068e418024 100644 --- a/dom/media/mediasource/TrackBuffer.h +++ b/dom/media/mediasource/TrackBuffer.h @@ -80,6 +80,11 @@ public: // Call ResetDecode() on each decoder in mDecoders. void ResetDecode(); + // Run MSE Reset Parser State Algorithm. + // 3.5.2 Reset Parser State + // http://w3c.github.io/media-source/#sourcebuffer-reset-parser-state + void ResetParserState(); + // Returns a reference to mInitializedDecoders, used by MediaSourceReader // to select decoders. // TODO: Refactor to a cleaner interface between TrackBuffer and MediaSourceReader. From 52d3bbb9ae5bb0de6530b5f16f389bcec3587c8b Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:48:25 +1100 Subject: [PATCH 012/133] Bug 1120086: Re-open SourceBuffer after call to appendBuffer if in ended state. r=cajbir --HG-- extra : rebase_source : 56923ceb5f28dadeda981b8365f277320afc72ba --- dom/media/mediasource/SourceBuffer.cpp | 2 +- dom/media/mediasource/SourceBufferResource.cpp | 1 + dom/media/mediasource/TrackBuffer.cpp | 9 +++++++++ dom/media/mediasource/TrackBuffer.h | 2 ++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp index 83d32e79d87b..7895b84260bc 100644 --- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -233,7 +233,7 @@ SourceBuffer::Ended() MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(IsAttached()); MSE_DEBUG("SourceBuffer(%p)::Ended", this); - mTrackBuffer->DiscardDecoder(); + mTrackBuffer->EndCurrentDecoder(); } SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType) diff --git a/dom/media/mediasource/SourceBufferResource.cpp b/dom/media/mediasource/SourceBufferResource.cpp index b451d1ea2ca6..d1eb47e3e688 100644 --- a/dom/media/mediasource/SourceBufferResource.cpp +++ b/dom/media/mediasource/SourceBufferResource.cpp @@ -205,6 +205,7 @@ SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength) SBR_DEBUG("SourceBufferResource(%p)::AppendData(aData=%p, aLength=%u)", this, aData, aLength); ReentrantMonitorAutoEnter mon(mMonitor); mInputBuffer.AppendItem(aData, aLength); + mEnded = false; mon.NotifyAll(); } diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index b943b2d6e873..980da0ed6380 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -548,6 +548,15 @@ TrackBuffer::DiscardDecoder() mCurrentDecoder = nullptr; } +void +TrackBuffer::EndCurrentDecoder() +{ + ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor()); + if (mCurrentDecoder) { + mCurrentDecoder->GetResource()->Ended(); + } +} + void TrackBuffer::Detach() { diff --git a/dom/media/mediasource/TrackBuffer.h b/dom/media/mediasource/TrackBuffer.h index 8b068e418024..a577e17faed0 100644 --- a/dom/media/mediasource/TrackBuffer.h +++ b/dom/media/mediasource/TrackBuffer.h @@ -61,6 +61,8 @@ public: // Mark the current decoder's resource as ended, clear mCurrentDecoder and // reset mLast{Start,End}Timestamp. void DiscardDecoder(); + // Mark the current decoder's resource as ended. + void EndCurrentDecoder(); void Detach(); From 2ceb0057aff2bd9e6755535a19fc43dc16fa435c Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:49:01 +1100 Subject: [PATCH 013/133] Bug 1120075: Use Movie Extend Header's duration as fallback when available. r=kentuckyfriedtakahe --HG-- extra : rebase_source : 54e554e183d9072908ec02b6c14de5c2f46e8204 --- media/libstagefright/binding/mp4_demuxer.cpp | 7 ++++ .../av/include/media/stagefright/MetaData.h | 1 + .../media/libstagefright/MPEG4Extractor.cpp | 40 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/media/libstagefright/binding/mp4_demuxer.cpp b/media/libstagefright/binding/mp4_demuxer.cpp index d2505d627070..e77e01c026e6 100644 --- a/media/libstagefright/binding/mp4_demuxer.cpp +++ b/media/libstagefright/binding/mp4_demuxer.cpp @@ -144,6 +144,13 @@ MP4Demuxer::Init() sp metaData = e->getMetaData(); mCrypto.Update(metaData); + int64_t movieDuration; + if (!mVideoConfig.duration && !mAudioConfig.duration && + metaData->findInt64(kKeyMovieDuration, &movieDuration)) { + // No duration were found in either tracks, use movie extend header box one. + mVideoConfig.duration = mAudioConfig.duration = movieDuration; + } + return mPrivate->mAudio.get() || mPrivate->mVideo.get(); } diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h b/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h index 9c78a6948b3a..30d969d29fda 100644 --- a/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h @@ -67,6 +67,7 @@ enum { kKeyDriftTime = 'dftT', // int64_t (usecs) kKeyAnchorTime = 'ancT', // int64_t (usecs) kKeyDuration = 'dura', // int64_t (usecs) + kKeyMovieDuration = 'mdur', // int64_t (usecs) kKeyColorFormat = 'colf', kKeyPlatformPrivate = 'priv', // pointer kKeyDecoderComponent = 'decC', // cstring diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp index 06ebd1b4fec5..34adcf21b62f 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -1697,6 +1697,46 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('m', 'e', 'h', 'd'): + { + if (chunk_data_size < 8) { + return ERROR_MALFORMED; + } + + uint8_t version; + if (mDataSource->readAt( + data_offset, &version, sizeof(version)) + < (ssize_t)sizeof(version)) { + return ERROR_IO; + } + if (version > 1) { + break; + } + int64_t duration = 0; + if (version == 1) { + if (mDataSource->readAt( + data_offset + 4, &duration, sizeof(duration)) + < (ssize_t)sizeof(duration)) { + return ERROR_IO; + } + duration = ntoh64(duration); + } else { + uint32_t duration32; + if (mDataSource->readAt( + data_offset + 4, &duration32, sizeof(duration32)) + < (ssize_t)sizeof(duration32)) { + return ERROR_IO; + } + duration = ntohl(duration32); + } + if (duration) { + mFileMetaData->setInt64(kKeyMovieDuration, duration * 1000LL); + } + + *offset += chunk_size; + break; + } + case FOURCC('m', 'd', 'a', 't'): { ALOGV("mdat chunk, drm: %d", mIsDrm); From 9e51efea6ef2c2761e3aa7659f758200b25f6f6e Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:49:01 +1100 Subject: [PATCH 014/133] Bug 1120075: Update w3c web reference tests. r=cajbir --HG-- extra : rebase_source : db9a8da697e01c11c9f9749917c5d75dccd07078 --- .../meta/media-source/mediasource-redundant-seek.html.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini b/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini index 93ab21a4c1f2..395610280da0 100644 --- a/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini @@ -6,7 +6,7 @@ TIMEOUT [Test redundant fully prebuffered seek] expected: - if (os == "win") and (version != "5.1.2600"): FAIL - if os == "mac": FAIL + if (os == "win") and (version != "5.1.2600"): PASS + if os == "mac": PASS TIMEOUT From b0638f45f3e087f65556439bfafcf597190a56c5 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:49:01 +1100 Subject: [PATCH 015/133] Bug 1119757: Allow seeking on media with infinite duration. r=cpearce MSE defines content to have infinite duration when no duration is defined. And MSE is always seekable within the buffered range, regardless of the duration --HG-- extra : rebase_source : 369cce10504b125716af0a60e07d1d1144117f59 --- dom/media/MediaDecoderStateMachine.cpp | 36 ++++++++++++++++++-------- dom/media/MediaDecoderStateMachine.h | 13 ++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index f486cd76e126..ef7d4bbb8239 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -188,6 +188,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mPlayDuration(0), mStartTime(-1), mEndTime(-1), + mDurationSet(false), mFragmentEndTime(-1), mReader(aReader), mCurrentFrameTime(0), @@ -1408,6 +1409,14 @@ int64_t MediaDecoderStateMachine::GetDuration() return mEndTime - mStartTime; } +int64_t MediaDecoderStateMachine::GetEndTime() +{ + if (mEndTime == -1 && mDurationSet) { + return INT64_MAX; + } + return mEndTime; +} + void MediaDecoderStateMachine::SetDuration(int64_t aDuration) { NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(), @@ -1415,13 +1424,21 @@ void MediaDecoderStateMachine::SetDuration(int64_t aDuration) AssertCurrentThreadInMonitor(); if (aDuration == -1) { + mDurationSet = false; return; } + mDurationSet = true; + if (mStartTime == -1) { SetStartTime(0); } + if (aDuration == INT64_MAX) { + mEndTime = -1; + return; + } + mEndTime = mStartTime + aDuration; } @@ -1735,12 +1752,13 @@ MediaDecoderStateMachine::StartSeek(const SeekTarget& aTarget) } // Bound the seek time to be inside the media range. + int64_t end = GetEndTime(); NS_ASSERTION(mStartTime != -1, "Should know start time by now"); - NS_ASSERTION(mEndTime != -1, "Should know end time by now"); + NS_ASSERTION(end != -1, "Should know end time by now"); int64_t seekTime = aTarget.mTime + mStartTime; - seekTime = std::min(seekTime, mEndTime); + seekTime = std::min(seekTime, end); seekTime = std::max(mStartTime, seekTime); - NS_ASSERTION(seekTime >= mStartTime && seekTime <= mEndTime, + NS_ASSERTION(seekTime >= mStartTime && seekTime <= end, "Can only seek in range [0,duration]"); mSeekTarget = SeekTarget(seekTime, aTarget.mType); @@ -2188,7 +2206,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata() return NS_ERROR_FAILURE; } mDecoder->StartProgressUpdates(); - mGotDurationFromMetaData = (GetDuration() != -1); + mGotDurationFromMetaData = (GetDuration() != -1) || mDurationSet; if (mGotDurationFromMetaData) { // We have all the information required: duration and size @@ -2300,12 +2318,8 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame() } NS_ASSERTION(mStartTime != -1, "Must have start time"); - MOZ_ASSERT((!HasVideo() && !HasAudio()) || - !(mDecoder->IsMediaSeekable() && mDecoder->IsTransportSeekable()) || - mEndTime != -1, - "Active seekable media should have end time"); MOZ_ASSERT(!(mDecoder->IsMediaSeekable() && mDecoder->IsTransportSeekable()) || - GetDuration() != -1, + (GetDuration() != -1) || mDurationSet, "Seekable media should have duration"); DECODER_LOG("Media goes from %lld to %lld (duration %lld) " "transportSeekable=%d, mediaSeekable=%d", @@ -2429,7 +2443,7 @@ void MediaDecoderStateMachine::DecodeSeek() // the reader, since it could do I/O or deadlock some other way. res = mReader->ResetDecode(); if (NS_SUCCEEDED(res)) { - mReader->Seek(seekTime, mStartTime, mEndTime, mCurrentTimeBeforeSeek) + mReader->Seek(seekTime, mStartTime, GetEndTime(), mCurrentTimeBeforeSeek) ->Then(DecodeTaskQueue(), __func__, this, &MediaDecoderStateMachine::OnSeekCompleted, &MediaDecoderStateMachine::OnSeekFailed); @@ -3244,7 +3258,7 @@ void MediaDecoderStateMachine::SetStartTime(int64_t aStartTimeUsecs) mStartTime = 0; if (aStartTimeUsecs != 0) { mStartTime = aStartTimeUsecs; - if (mGotDurationFromMetaData) { + if (mGotDurationFromMetaData && GetEndTime() != INT64_MAX) { NS_ASSERTION(mEndTime != -1, "We should have mEndTime as supplied duration here"); // We were specified a duration from a Content-Duration HTTP header. diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index a2f995a07577..280b067a0ab4 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -171,11 +171,18 @@ public: // must be obtained before calling this. It is in units of microseconds. int64_t GetDuration(); + // Time of the last frame in the media, in microseconds or INT64_MAX if + // media has an infinite duration. + // Accessed on state machine, decode, and main threads. + // Access controlled by decoder monitor. + int64_t GetEndTime(); + // Called from the main thread to set the duration of the media resource // if it is able to be obtained via HTTP headers. Called from the // state machine thread to set the duration if it is obtained from the // media metadata. The decoder monitor must be obtained before calling this. // aDuration is in microseconds. + // A value of INT64_MAX will be treated as infinity. void SetDuration(int64_t aDuration); // Called while decoding metadata to set the end time of the media @@ -832,8 +839,14 @@ protected: // Time of the last frame in the media, in microseconds. This is the // end time of the last frame in the media. Accessed on state // machine, decode, and main threads. Access controlled by decoder monitor. + // It will be set to -1 if the duration is infinite int64_t mEndTime; + // Will be set when SetDuration has been called with a value != -1 + // mDurationSet false doesn't indicate that we do not have a valid duration + // as mStartTime and mEndTime could have been set separately. + bool mDurationSet; + // Position to seek to in microseconds when the seek state transition occurs. // The decoder monitor lock must be obtained before reading or writing // this value. Accessed on main and decode thread. From 776799bb61b64c52ae0370370d8816b2a7d35bb2 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:49:01 +1100 Subject: [PATCH 016/133] Bug 1119757: MSE: handle duration of 0 in metadata as infinity. r=mattwoodrow --HG-- extra : rebase_source : 6bc09d18b0b5d8144c8242d94f1ebd92d1f0efa5 --- dom/media/mediasource/MediaSourceDecoder.cpp | 18 +++++++++++++----- dom/media/mediasource/MediaSourceReader.cpp | 6 ++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp index 196dcb1ad20d..02cc8751b29c 100644 --- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -214,7 +214,10 @@ MediaSourceDecoder::SetDecodedDuration(int64_t aDuration) return; } double duration = aDuration; - duration /= USECS_PER_S; + // A duration of -1 is +Infinity. + if (aDuration >= 0) { + duration /= USECS_PER_S; + } SetMediaSourceDuration(duration); } @@ -223,13 +226,18 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration) { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); double oldDuration = mMediaSourceDuration; - mMediaSourceDuration = aDuration; - mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S); + if (aDuration >= 0) { + mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S); + mMediaSourceDuration = aDuration; + } else { + mDecoderStateMachine->SetDuration(INT64_MAX); + mMediaSourceDuration = PositiveInfinity(); + } if (NS_IsMainThread()) { - DurationChanged(oldDuration, aDuration); + DurationChanged(oldDuration, mMediaSourceDuration); } else { nsRefPtr task = - new DurationChangedRunnable(this, oldDuration, aDuration); + new DurationChangedRunnable(this, oldDuration, mMediaSourceDuration); NS_DispatchToMainThread(task); } } diff --git a/dom/media/mediasource/MediaSourceReader.cpp b/dom/media/mediasource/MediaSourceReader.cpp index 294d33f61ab3..c1f15cf8fa2a 100644 --- a/dom/media/mediasource/MediaSourceReader.cpp +++ b/dom/media/mediasource/MediaSourceReader.cpp @@ -845,9 +845,11 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) this, mVideoReader.get(), maxDuration); } - if (maxDuration != -1) { - static_cast(mDecoder)->SetDecodedDuration(maxDuration); + if (!maxDuration) { + // Treat a duration of 0 as infinity + maxDuration = -1; } + static_cast(mDecoder)->SetDecodedDuration(maxDuration); *aInfo = mInfo; *aTags = nullptr; // TODO: Handle metadata. From eb3a3abff9d8998d7928854559b7a852fc2777ae Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:49:02 +1100 Subject: [PATCH 017/133] Bug 1120079: Do not call Range Removal algorithm after endOfStream. r=cajbir --HG-- extra : rebase_source : 6a77cda482230943d88df228425e23e20f8860e0 --- dom/media/mediasource/MediaSource.cpp | 9 +++--- dom/media/mediasource/MediaSource.h | 7 ++++- dom/media/mediasource/MediaSourceDecoder.cpp | 31 +++++++++++++------- dom/media/mediasource/MediaSourceDecoder.h | 3 +- dom/media/mediasource/SourceBuffer.cpp | 2 +- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index 51e51d25c36d..8f5eef6dfd9a 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -189,15 +189,15 @@ MediaSource::SetDuration(double aDuration, ErrorResult& aRv) aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } - SetDuration(aDuration); + SetDuration(aDuration, MSRangeRemovalAction::RUN); } void -MediaSource::SetDuration(double aDuration) +MediaSource::SetDuration(double aDuration, MSRangeRemovalAction aAction) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("MediaSource(%p)::SetDuration(aDuration=%f)", this, aDuration); - mDecoder->SetMediaSourceDuration(aDuration); + mDecoder->SetMediaSourceDuration(aDuration, aAction); } already_AddRefed @@ -285,7 +285,8 @@ MediaSource::EndOfStream(const Optional& aError, Er mSourceBuffers->Ended(); mDecoder->Ended(); if (!aError.WasPassed()) { - mDecoder->SetMediaSourceDuration(mSourceBuffers->GetHighestBufferedEndTime()); + mDecoder->SetMediaSourceDuration(mSourceBuffers->GetHighestBufferedEndTime(), + MSRangeRemovalAction::SKIP); if (aRv.Failed()) { return; } diff --git a/dom/media/mediasource/MediaSource.h b/dom/media/mediasource/MediaSource.h index 7011a2c6cb13..c6ddcaf7779b 100644 --- a/dom/media/mediasource/MediaSource.h +++ b/dom/media/mediasource/MediaSource.h @@ -29,6 +29,11 @@ namespace mozilla { class ErrorResult; template class AsyncEventRunner; +enum MSRangeRemovalAction: uint8_t { + RUN = 0, + SKIP = 1 +}; + namespace dom { class GlobalObject; @@ -129,7 +134,7 @@ private: void InitializationEvent(); // SetDuration with no checks. - void SetDuration(double aDuration); + void SetDuration(double aDuration, MSRangeRemovalAction aAction); nsRefPtr mSourceBuffers; nsRefPtr mActiveSourceBuffers; diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp index 02cc8751b29c..d0165d7adcbc 100644 --- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -173,9 +173,9 @@ MediaSourceDecoder::IsExpectingMoreData() class DurationChangedRunnable : public nsRunnable { public: - explicit DurationChangedRunnable(MediaSourceDecoder* aDecoder, - double aOldDuration, - double aNewDuration) + DurationChangedRunnable(MediaSourceDecoder* aDecoder, + double aOldDuration, + double aNewDuration) : mDecoder(aDecoder) , mOldDuration(aOldDuration) , mNewDuration(aNewDuration) @@ -218,11 +218,13 @@ MediaSourceDecoder::SetDecodedDuration(int64_t aDuration) if (aDuration >= 0) { duration /= USECS_PER_S; } - SetMediaSourceDuration(duration); + // No need to call Range Removal algorithm as this is called following + // ReadMetadata and nothing has been added to the source buffers yet. + SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP); } void -MediaSourceDecoder::SetMediaSourceDuration(double aDuration) +MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction) { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); double oldDuration = mMediaSourceDuration; @@ -233,12 +235,21 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration) mDecoderStateMachine->SetDuration(INT64_MAX); mMediaSourceDuration = PositiveInfinity(); } - if (NS_IsMainThread()) { - DurationChanged(oldDuration, mMediaSourceDuration); + if (aAction == MSRangeRemovalAction::SKIP) { + if (NS_IsMainThread()) { + MediaDecoder::DurationChanged(); + } else { + nsCOMPtr task = NS_NewRunnableMethod(this, &MediaDecoder::DurationChanged); + NS_DispatchToMainThread(task); + } } else { - nsRefPtr task = - new DurationChangedRunnable(this, oldDuration, mMediaSourceDuration); - NS_DispatchToMainThread(task); + if (NS_IsMainThread()) { + DurationChanged(oldDuration, mMediaSourceDuration); + } else { + nsRefPtr task = + new DurationChangedRunnable(this, oldDuration, mMediaSourceDuration); + NS_DispatchToMainThread(task); + } } } diff --git a/dom/media/mediasource/MediaSourceDecoder.h b/dom/media/mediasource/MediaSourceDecoder.h index 6974698c5d8c..500c7dacaac4 100644 --- a/dom/media/mediasource/MediaSourceDecoder.h +++ b/dom/media/mediasource/MediaSourceDecoder.h @@ -21,6 +21,7 @@ class MediaResource; class MediaDecoderStateMachine; class SourceBufferDecoder; class TrackBuffer; +enum MSRangeRemovalAction : uint8_t; namespace dom { @@ -56,7 +57,7 @@ public: bool IsExpectingMoreData() MOZ_OVERRIDE; void SetDecodedDuration(int64_t aDuration); - void SetMediaSourceDuration(double aDuration); + void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction); double GetMediaSourceDuration(); void DurationChanged(double aOldDuration, double aNewDuration); diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp index 7895b84260bc..953a57de16ee 100644 --- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -332,7 +332,7 @@ SourceBuffer::CheckEndTime() // Check if we need to update mMediaSource duration double endTime = GetBufferedEnd(); if (endTime > mMediaSource->Duration()) { - mMediaSource->SetDuration(endTime); + mMediaSource->SetDuration(endTime, MSRangeRemovalAction::SKIP); } } From 59151e0b1cc64b2ab2840b866084c79876394b83 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Fri, 16 Jan 2015 23:49:02 +1100 Subject: [PATCH 018/133] Bug 1120282: Do not fire durationchange during call to ReadMetadata. r=mattwoodrow --HG-- extra : rebase_source : cf8bdb932aac0661de21c7fa4eba7d50b14b3dbc --- dom/media/mediasource/MediaSourceDecoder.cpp | 25 +++++++++++++++----- dom/media/mediasource/MediaSourceDecoder.h | 5 ++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp index d0165d7adcbc..c35b1f35e741 100644 --- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -218,9 +218,7 @@ MediaSourceDecoder::SetDecodedDuration(int64_t aDuration) if (aDuration >= 0) { duration /= USECS_PER_S; } - // No need to call Range Removal algorithm as this is called following - // ReadMetadata and nothing has been added to the source buffers yet. - SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP); + DoSetMediaSourceDuration(duration); } void @@ -228,6 +226,13 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalActio { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); double oldDuration = mMediaSourceDuration; + DoSetMediaSourceDuration(aDuration); + ScheduleDurationChange(oldDuration, aDuration, aAction); +} + +void +MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration) +{ if (aDuration >= 0) { mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S); mMediaSourceDuration = aDuration; @@ -235,19 +240,27 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalActio mDecoderStateMachine->SetDuration(INT64_MAX); mMediaSourceDuration = PositiveInfinity(); } +} + +void +MediaSourceDecoder::ScheduleDurationChange(double aOldDuration, + double aNewDuration, + MSRangeRemovalAction aAction) +{ if (aAction == MSRangeRemovalAction::SKIP) { if (NS_IsMainThread()) { MediaDecoder::DurationChanged(); } else { - nsCOMPtr task = NS_NewRunnableMethod(this, &MediaDecoder::DurationChanged); + nsCOMPtr task = + NS_NewRunnableMethod(this, &MediaDecoder::DurationChanged); NS_DispatchToMainThread(task); } } else { if (NS_IsMainThread()) { - DurationChanged(oldDuration, mMediaSourceDuration); + DurationChanged(aOldDuration, aNewDuration); } else { nsRefPtr task = - new DurationChangedRunnable(this, oldDuration, mMediaSourceDuration); + new DurationChangedRunnable(this, aOldDuration, aNewDuration); NS_DispatchToMainThread(task); } } diff --git a/dom/media/mediasource/MediaSourceDecoder.h b/dom/media/mediasource/MediaSourceDecoder.h index 500c7dacaac4..6ac68a54c676 100644 --- a/dom/media/mediasource/MediaSourceDecoder.h +++ b/dom/media/mediasource/MediaSourceDecoder.h @@ -80,6 +80,11 @@ public: bool IsActiveReader(MediaDecoderReader* aReader); private: + void DoSetMediaSourceDuration(double aDuration); + void ScheduleDurationChange(double aOldDuration, + double aNewDuration, + MSRangeRemovalAction aAction); + // The owning MediaSource holds a strong reference to this decoder, and // calls Attach/DetachMediaSource on this decoder to set and clear // mMediaSource. From dd5974bda1ff86432ce3dc8d51710e4ae59d30e4 Mon Sep 17 00:00:00 2001 From: Anuj Agarwal Date: Thu, 15 Jan 2015 05:12:00 +0100 Subject: [PATCH 019/133] Bug 1075702 - Fixed implementation of Element.setAttributeNode(). r=bz --- dom/base/Attr.cpp | 4 +- dom/base/Element.cpp | 4 +- dom/base/nsDOMAttributeMap.cpp | 85 +++++++++++++++++-------------- dom/base/nsDOMAttributeMap.h | 2 + dom/base/test/test_bug469304.html | 36 ++++++------- 5 files changed, 71 insertions(+), 60 deletions(-) diff --git a/dom/base/Attr.cpp b/dom/base/Attr.cpp index f7ad7fef09d5..06226b66f2b4 100644 --- a/dom/base/Attr.cpp +++ b/dom/base/Attr.cpp @@ -183,7 +183,7 @@ Attr::GetValue(nsAString& aValue) { Element* element = GetElement(); if (element) { - nsCOMPtr nameAtom = GetNameAtom(element); + nsCOMPtr nameAtom = mNodeInfo->NameAtom(); element->GetAttr(mNodeInfo->NamespaceID(), nameAtom, aValue); } else { @@ -202,7 +202,7 @@ Attr::SetValue(const nsAString& aValue, ErrorResult& aRv) return; } - nsCOMPtr nameAtom = GetNameAtom(element); + nsCOMPtr nameAtom = mNodeInfo->NameAtom(); aRv = element->SetAttr(mNodeInfo->NamespaceID(), nameAtom, mNodeInfo->GetPrefixAtom(), diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 6b27f7e6bf04..df79f4d8d5bf 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1199,7 +1199,9 @@ Element::RemoveAttributeNode(Attr& aAttribute, ErrorResult& aError) { OwnerDoc()->WarnOnceAbout(nsIDocument::eRemoveAttributeNode); - return Attributes()->RemoveNamedItem(aAttribute.NodeName(), aError); + nsAutoString nameSpaceURI; + aAttribute.NodeInfo()->GetNamespaceURI(nameSpaceURI); + return Attributes()->RemoveNamedItemNS(nameSpaceURI, aAttribute.NodeInfo()->LocalName(), aError); } void diff --git a/dom/base/nsDOMAttributeMap.cpp b/dom/base/nsDOMAttributeMap.cpp index 1d63af7d7789..53ab47b46ca8 100644 --- a/dom/base/nsDOMAttributeMap.cpp +++ b/dom/base/nsDOMAttributeMap.cpp @@ -316,46 +316,51 @@ nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr, } // Get nodeinfo and preexisting attribute (if it exists) - nsAutoString name; - nsRefPtr ni; + nsRefPtr oldNi; + + if (!aWithNS) { + nsAutoString name; + aAttr.GetName(name); + oldNi = mContent->GetExistingAttrNameFromQName(name); + } + else { + uint32_t i, count = mContent->GetAttrCount(); + for (i = 0; i < count; ++i) { + const nsAttrName* name = mContent->GetAttrNameAt(i); + int32_t attrNS = name->NamespaceID(); + nsIAtom* nameAtom = name->LocalName(); + + // we're purposefully ignoring the prefix. + if (aAttr.NodeInfo()->Equals(nameAtom, attrNS)) { + oldNi = mContent->NodeInfo()->NodeInfoManager()-> + GetNodeInfo(nameAtom, name->GetPrefix(), aAttr.NodeInfo()->NamespaceID(), + nsIDOMNode::ATTRIBUTE_NODE); + break; + } + } + } nsRefPtr attr; - // SetNamedItemNS() - if (aWithNS) { - // Return existing attribute, if present - ni = aAttr.NodeInfo(); - if (mContent->HasAttr(ni->NamespaceID(), ni->NameAtom())) { - attr = RemoveAttribute(ni); + if (oldNi) { + nsRefPtr oldAttr = GetAttribute(oldNi, true); + + if (oldAttr == &aAttr) { + return oldAttr.forget(); } - } else { // SetNamedItem() - aAttr.GetName(name); - // get node-info of old attribute - ni = mContent->GetExistingAttrNameFromQName(name); - if (ni) { - attr = RemoveAttribute(ni); - } - else { - if (mContent->IsInHTMLDocument() && - mContent->IsHTML()) { - nsContentUtils::ASCIIToLower(name); - } - - rv = mContent->NodeInfo()->NodeInfoManager()-> - GetNodeInfo(name, nullptr, kNameSpaceID_None, - nsIDOMNode::ATTRIBUTE_NODE, getter_AddRefs(ni)); - if (NS_FAILED(rv)) { - aError.Throw(rv); - return nullptr; - } - // value is already empty + if (oldAttr) { + attr = RemoveNamedItem(oldNi, aError); + NS_ASSERTION(attr->NodeInfo()->NameAndNamespaceEquals(oldNi), + "RemoveNamedItem() called, attr->NodeInfo() should be equal to oldNi!"); } } nsAutoString value; aAttr.GetValue(value); + nsRefPtr ni = aAttr.NodeInfo(); + // Add the new attribute to the attribute map before updating // its value in the element. @see bug 364413. nsAttrKey attrkey(ni->NamespaceID(), ni->NameAtom()); @@ -373,6 +378,15 @@ nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr, return attr.forget(); } +already_AddRefed +nsDOMAttributeMap::RemoveNamedItem(NodeInfo* aNodeInfo, ErrorResult& aError) +{ + nsRefPtr attribute = GetAttribute(aNodeInfo, true); + // This removes the attribute node from the attribute map. + aError = mContent->UnsetAttr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom(), true); + return attribute.forget(); +} + NS_IMETHODIMP nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName, nsIDOMAttr** aReturn) @@ -398,11 +412,7 @@ nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName, ErrorResult& aError) return nullptr; } - nsRefPtr attribute = GetAttribute(ni, true); - - // This removes the attribute node from the attribute map. - aError = mContent->UnsetAttr(ni->NamespaceID(), ni->NameAtom(), true); - return attribute.forget(); + return RemoveNamedItem(ni, aError); } @@ -500,6 +510,7 @@ nsDOMAttributeMap::GetAttrNodeInfo(const nsAString& aNamespaceURI, int32_t attrNS = name->NamespaceID(); nsIAtom* nameAtom = name->LocalName(); + // we're purposefully ignoring the prefix. if (nameSpaceID == attrNS && nameAtom->Equals(aLocalName)) { nsRefPtr ni; @@ -536,11 +547,7 @@ nsDOMAttributeMap::RemoveNamedItemNS(const nsAString& aNamespaceURI, return nullptr; } - nsRefPtr attr = RemoveAttribute(ni); - mozilla::dom::NodeInfo* attrNi = attr->NodeInfo(); - mContent->UnsetAttr(attrNi->NamespaceID(), attrNi->NameAtom(), true); - - return attr.forget(); + return RemoveNamedItem(ni, aError); } uint32_t diff --git a/dom/base/nsDOMAttributeMap.h b/dom/base/nsDOMAttributeMap.h index ee67bc1b0803..00662638dd3d 100644 --- a/dom/base/nsDOMAttributeMap.h +++ b/dom/base/nsDOMAttributeMap.h @@ -152,6 +152,8 @@ public: return SetNamedItemInternal(aAttr, false, aError); } already_AddRefed + RemoveNamedItem(mozilla::dom::NodeInfo* aNodeInfo, ErrorResult& aError); + already_AddRefed RemoveNamedItem(const nsAString& aName, ErrorResult& aError); Attr* Item(uint32_t aIndex); diff --git a/dom/base/test/test_bug469304.html b/dom/base/test/test_bug469304.html index 234e90256027..09a1ce9014bc 100644 --- a/dom/base/test/test_bug469304.html +++ b/dom/base/test/test_bug469304.html @@ -27,8 +27,10 @@ function testGetAttribute() { document.body.setAttributeNode(a1); document.body.setAttributeNode(a2); var log = document.getElementById("log"); - is(document.body.getAttribute('aa'), "UPPERCASE", "Wrong value (1)"); - is(document.body.getAttribute('AA'), "UPPERCASE", "Wrong value (2)"); + is(document.body.getAttribute('aa'), null, "Attribute has the localName AA and not aa."); + is(document.body.getAttribute('AA'), null, "Attribute has the localName AA and not aa."); + is(document.body.getAttributeNS("", "aa"), null, "Attribute should have localName AA."); + is(document.body.getAttributeNS("", "AA"), "UPPERCASE", "Attribute should have value UPPERCASE!"); var s = ""; for (var i = 0; i < document.body.attributes.length; ++i) { @@ -40,13 +42,11 @@ function testGetAttribute() { is(document.body.getAttributeNode("aa"), document.body.getAttributeNode("AA"), "Wrong node!"); - document.body.getAttributeNode("AA").nodeValue = "FOO"; - is(document.body.getAttribute("AA"), "FOO", "Wrong value!"); + document.body.getAttributeNodeNS("", "AA").nodeValue = "FOO"; + is(document.body.getAttributeNS("", "AA"), "FOO", "Wrong value!"); - document.body.removeAttributeNode(document.body.getAttributeNode("AA")); - ok(!document.body.hasAttribute("AA"), "Should not have attribute!"); + document.body.removeAttributeNode(document.body.getAttributeNodeNS("", "AA")); ok(!document.body.getAttributeNode("AA"), "Should not have attribute node!"); - ok(!document.body.hasAttribute("aa"), "Should not have attribute!"); ok(!document.body.getAttributeNode("aa"), "Should not have attribute node!"); is(a2.nodeValue, "FOO", "Wrong value!"); @@ -54,10 +54,10 @@ function testGetAttribute() { is(a2.nodeValue, "UPPERCASE", "Wrong value!"); document.body.setAttributeNode(a2); - is(document.body.getAttribute("AA"), "UPPERCASE", "wrong value!"); - ok(document.body.getAttributeNode("AA"), "Should have attribute node!"); - is(document.body.getAttribute("aa"), "UPPERCASE", "wrong value!"); - ok(document.body.getAttributeNode("aa"), "Should have attribute node!"); + is(document.body.getAttributeNS("", "AA"), "UPPERCASE", "Wrong value!"); + ok(document.body.getAttributeNodeNS("", "AA"), "Should have attribute node!"); + is(document.body.getAttributeNS("", "aa"), null, "No attribute has the localName aa."); + ok(!document.body.getAttributeNodeNS("", "aa"), "Should not have attribute node!"); } testGetAttribute(); @@ -75,7 +75,7 @@ function testGetAttributeNodeMixedCase() { var a = div.ownerDocument.createAttribute("mixedCaseAttrib"); a.nodeValue = "x"; div.setAttributeNode(a); - return div.getAttribute("mixedCaseAttrib"); + return div.getAttributeNS("", "mixedCaseAttrib"); } is(testGetAttributeNodeMixedCase(), "x", "(2)"); @@ -114,7 +114,7 @@ function testAttribNodeNamePreservesCaseGetNode() { var a = body.ownerDocument.createAttribute("A"); a.nodeValue = "x"; body.setAttributeNode(a); - a = document.body.getAttributeNode("A"); + a = document.body.getAttributeNodeNS("", "A"); if (!a) return "FAIL"; var result = [ a.name, a.nodeName ]; @@ -127,14 +127,14 @@ function testAttribNodeNamePreservesCaseGetNode2() { var a = body.ownerDocument.createAttribute("B"); a.nodeValue = "x"; body.setAttributeNode(a); - a = document.body.getAttributeNode("B"); + a = document.body.getAttributeNodeNS("", "B"); if (!a) return "FAIL"; // Now create node second time a = body.ownerDocument.createAttribute("B"); a.nodeValue = "x"; body.setAttributeNode(a); - a = document.body.getAttributeNode("B"); + a = document.body.getAttributeNodeNS("", "B"); var result = [ a.name, a.nodeName ]; return result.join(","); } @@ -158,9 +158,9 @@ attrib.nodeValue = "XXX"; node.setAttributeNode(attrib); // Note, this is different to what WebKit does is((new XMLSerializer).serializeToString(node), - "
", "(9)"); -is(node.getAttributeNode('myAttrib').name, "myAttrib", "(10)"); -is(node.getAttributeNode('myattrib').name, "myAttrib", "(11)"); + "
", "(9)"); +is(node.getAttributeNodeNS("", "myAttrib").name, "myAttrib", "(10)"); +is(node.getAttributeNodeNS("", "myattrib"), null, "(11)"); is(attrib.name, "myAttrib", "(12)"); var o = document.createElement("div"); From cdc8bda73f64ec120d3e53060f1a140cf12b1f76 Mon Sep 17 00:00:00 2001 From: Anuj Agarwal Date: Thu, 15 Jan 2015 05:13:00 +0100 Subject: [PATCH 020/133] Bug 1075702 - Added mochitest for Element.setAttributeNode(). r=bz --- dom/base/test/mochitest.ini | 1 + dom/base/test/test_bug1075702.html | 69 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 dom/base/test/test_bug1075702.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 1fbf13f27311..8898d3105863 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -657,6 +657,7 @@ run-if = os == 'linux' [test_bug1008126.html] run-if = os == 'linux' [test_bug1057176.html] +[test_bug1075702.html] [test_bug1101364.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' [test_caretPositionFromPoint.html] diff --git a/dom/base/test/test_bug1075702.html b/dom/base/test/test_bug1075702.html new file mode 100644 index 000000000000..47817aa42409 --- /dev/null +++ b/dom/base/test/test_bug1075702.html @@ -0,0 +1,69 @@ + + + + + + Test for Bug 1075702 + + + + + + Mozilla Bug 1075702 +

+ +
+
+
+

From bb144e96bc99c3ab52c38df72a715feb71836b89 Mon Sep 17 00:00:00 2001
From: Dragana Damjanovic 
Date: Thu, 15 Jan 2015 01:21:00 +0100
Subject: [PATCH 021/133] Bug 1108971 - Fix parameter in call GetAddrInfo.
 r=sworkman

---
 netwerk/dns/nsHostResolver.cpp | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp
index 9a36663f591c..13c52f7637cf 100644
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -1380,10 +1380,6 @@ nsHostResolver::ThreadFunc(void *arg)
         LOG(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
              rec->host));
 
-        int flags = PR_AI_ADDRCONFIG;
-        if (!(rec->flags & RES_CANON_NAME))
-            flags |= PR_AI_NOCANONNAME;
-
         TimeStamp startTime = TimeStamp::Now();
         MOZ_EVENT_TRACER_EXEC(rec, "net::dns::resolve");
 
@@ -1397,10 +1393,10 @@ nsHostResolver::ThreadFunc(void *arg)
         // because PR_GetAddrInfoByName doesn't support PR_AF_INET6.
         bool disableIPv4 = rec->af == PR_AF_INET6;
         uint16_t af = disableIPv4 ? PR_AF_UNSPEC : rec->af;
-        nsresult status = GetAddrInfo(rec->host, af, flags, &ai, getTtl);
+        nsresult status = GetAddrInfo(rec->host, af, rec->flags, &ai, getTtl);
 #if defined(RES_RETRY_ON_FAILURE)
         if (NS_FAILED(status) && rs.Reset()) {
-            status = GetAddrInfo(rec->host, af, flags, &ai, getTtl);
+            status = GetAddrInfo(rec->host, af, rec->flags, &ai, getTtl);
         }
 #endif
 

From 1247e9d9233a5cf23d3dee1ac197d35847cfd1b9 Mon Sep 17 00:00:00 2001
From: Jehan 
Date: Thu, 15 Jan 2015 20:00:58 +0100
Subject: [PATCH 022/133] Bug 1114357 - Firefox does not understand XF86 ZoomIn
 and ZoomOut keys. r=masayuki

---
 dom/events/EventStateManager.cpp | 96 +++++++++++++++++++++-----------
 dom/events/EventStateManager.h   |  3 +
 2 files changed, 66 insertions(+), 33 deletions(-)

diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
index 65563f80e40a..b0684a2ca7a6 100644
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2674,6 +2674,67 @@ NodeAllowsClickThrough(nsINode* aNode)
 }
 #endif
 
+void
+EventStateManager::PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent,
+                                           nsEventStatus& aStatus,
+                                           bool dispatchedToContentProcess)
+{
+  if (aStatus == nsEventStatus_eConsumeNoDefault) {
+    return;
+  }
+
+  // XXX Currently, our automated tests don't support mKeyNameIndex.
+  //     Therefore, we still need to handle this with keyCode.
+  switch(aKeyboardEvent->keyCode) {
+    case NS_VK_TAB:
+    case NS_VK_F6:
+      // This is to prevent keyboard scrolling while alt modifier in use.
+      if (!aKeyboardEvent->IsAlt()) {
+        // Handling the tab event after it was sent to content is bad,
+        // because to the FocusManager the remote-browser looks like one
+        // element, so we would just move the focus to the next element
+        // in chrome, instead of handling it in content.
+        if (dispatchedToContentProcess)
+          break;
+
+        EnsureDocument(mPresContext);
+        nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+        if (fm && mDocument) {
+          // Shift focus forward or back depending on shift key
+          bool isDocMove =
+            aKeyboardEvent->IsControl() || aKeyboardEvent->keyCode == NS_VK_F6;
+          uint32_t dir = aKeyboardEvent->IsShift() ?
+            (isDocMove ? static_cast(nsIFocusManager::MOVEFOCUS_BACKWARDDOC) :
+                         static_cast(nsIFocusManager::MOVEFOCUS_BACKWARD)) :
+            (isDocMove ? static_cast(nsIFocusManager::MOVEFOCUS_FORWARDDOC) :
+                         static_cast(nsIFocusManager::MOVEFOCUS_FORWARD));
+          nsCOMPtr result;
+          fm->MoveFocus(mDocument->GetWindow(), nullptr, dir,
+                        nsIFocusManager::FLAG_BYKEY,
+                        getter_AddRefs(result));
+        }
+        aStatus = nsEventStatus_eConsumeNoDefault;
+      }
+      return;
+    case 0:
+      // We handle keys with no specific keycode value below.
+      break;
+    default:
+      return;
+  }
+
+  switch(aKeyboardEvent->mKeyNameIndex) {
+    case KEY_NAME_INDEX_ZoomIn:
+    case KEY_NAME_INDEX_ZoomOut:
+      ChangeFullZoom(
+        aKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_ZoomIn ? 1 : -1);
+      aStatus = nsEventStatus_eConsumeNoDefault;
+      break;
+    default:
+      break;
+  }
+}
+
 nsresult
 EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
                                    WidgetEvent* aEvent,
@@ -3185,40 +3246,9 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
     break;
 
   case NS_KEY_PRESS:
-    if (nsEventStatus_eConsumeNoDefault != *aStatus) {
+    {
       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
-      //This is to prevent keyboard scrolling while alt modifier in use.
-      if (!keyEvent->IsAlt()) {
-        switch(keyEvent->keyCode) {
-          case NS_VK_TAB:
-          case NS_VK_F6:
-            // Handling the tab event after it was sent to content is bad,
-            // because to the FocusManager the remote-browser looks like one
-            // element, so we would just move the focus to the next element
-            // in chrome, instead of handling it in content.
-            if (dispatchedToContentProcess)
-              break;
-
-            EnsureDocument(mPresContext);
-            nsIFocusManager* fm = nsFocusManager::GetFocusManager();
-            if (fm && mDocument) {
-              // Shift focus forward or back depending on shift key
-              bool isDocMove =
-                keyEvent->IsControl() || keyEvent->keyCode == NS_VK_F6;
-              uint32_t dir = keyEvent->IsShift() ?
-                  (isDocMove ? static_cast(nsIFocusManager::MOVEFOCUS_BACKWARDDOC) :
-                               static_cast(nsIFocusManager::MOVEFOCUS_BACKWARD)) :
-                  (isDocMove ? static_cast(nsIFocusManager::MOVEFOCUS_FORWARDDOC) :
-                               static_cast(nsIFocusManager::MOVEFOCUS_FORWARD));
-              nsCOMPtr result;
-              fm->MoveFocus(mDocument->GetWindow(), nullptr, dir,
-                            nsIFocusManager::FLAG_BYKEY,
-                            getter_AddRefs(result));
-            }
-            *aStatus = nsEventStatus_eConsumeNoDefault;
-            break;
-        }
-      }
+      PostHandleKeyboardEvent(keyEvent, *aStatus, dispatchedToContentProcess);
     }
     break;
 
diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h
index 68c0809853b3..3b509f0a8a5a 100644
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -815,6 +815,9 @@ private:
   static PLDHashOperator ResetLastOverForContent(const uint32_t& aIdx,
                                                  nsRefPtr& aChunk,
                                                  void* aClosure);
+  void PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent,
+                               nsEventStatus& aStatus,
+                               bool dispatchedToContentProcess);
 
   int32_t     mLockCursor;
 

From e9788e3d05b1d70453e7f58131c10196139ffc81 Mon Sep 17 00:00:00 2001
From: Makoto Kato 
Date: Fri, 16 Jan 2015 23:07:09 +0900
Subject: [PATCH 023/133] Bug 1121829 - Support redirection of kernel32.dll for
 hooking function. r=dmajor

---
 xpcom/build/nsWindowsDllInterceptor.h | 29 +++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/xpcom/build/nsWindowsDllInterceptor.h b/xpcom/build/nsWindowsDllInterceptor.h
index a5f3869ce74f..cadf32e5307b 100644
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/xpcom/build/nsWindowsDllInterceptor.h
@@ -136,6 +136,8 @@ public:
       return false;
     }
 
+    fn = ResolveRedirectedAddress(fn);
+
     // Ensure we can read and write starting at fn - 5 (for the long jmp we're
     // going to write) and ending at fn + 2 (for the short jmp up to the long
     // jmp).
@@ -202,6 +204,18 @@ public:
 
     return true;
   }
+
+private:
+  static byteptr_t ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
+  {
+    // If function entry is jmp [disp32] such as used by kernel32,
+    // we resolve redirected address from import table.
+    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
+      return (byteptr_t)(**((uint32_t**) (aOriginalFunction + 2)));
+    }
+
+    return aOriginalFunction;
+  }
 #else
   bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
   {
@@ -312,6 +326,8 @@ public:
       return false;
     }
 
+    pAddr = ResolveRedirectedAddress((byteptr_t)pAddr);
+
     CreateTrampoline(pAddr, aHookDest, aOrigFunc);
     if (!*aOrigFunc) {
       //printf ("CreateTrampoline failed\n");
@@ -653,6 +669,19 @@ protected:
 
     return p;
   }
+
+  static void* ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
+  {
+#if defined(_M_IX86)
+    // If function entry is jmp [disp32] such as used by kernel32,
+    // we resolve redirected address from import table.
+    if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
+      return (void*)(**((uint32_t**) (aOriginalFunction + 2)));
+    }
+#endif
+
+    return aOriginalFunction;
+  }
 };
 
 } // namespace internal

From c8cab92a680798816ba0a77cae5e3eeae3f0873e Mon Sep 17 00:00:00 2001
From: Jon Coppeard 
Date: Fri, 16 Jan 2015 14:34:16 +0000
Subject: [PATCH 024/133] Bug 650161 - Fix misc build errors with compacting GC
 enabled r=terrence

---
 js/src/gc/GCInternals.h            | 2 +-
 js/src/gc/Marking.cpp              | 1 +
 js/src/jsapi-tests/testWeakMap.cpp | 9 +++------
 js/src/jsgc.cpp                    | 6 ++++--
 4 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h
index 81309133855a..0a718a9ac32b 100644
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -145,7 +145,7 @@ CheckHashTablesAfterMovingGC(JSRuntime *rt);
 
 #ifdef JSGC_COMPACTING
 struct MovingTracer : JSTracer {
-    MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapKeysValues) {}
+    explicit MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapKeysValues) {}
 
     static void Visit(JSTracer *jstrc, void **thingp, JSGCTraceKind kind);
     static bool IsMovingTracer(JSTracer *trc) {
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
index eec435e71940..94500b5ee718 100644
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -10,6 +10,7 @@
 
 #include "jsprf.h"
 
+#include "gc/GCInternals.h"
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
diff --git a/js/src/jsapi-tests/testWeakMap.cpp b/js/src/jsapi-tests/testWeakMap.cpp
index 9569dd677b74..a337bcc3ec63 100644
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -129,6 +129,9 @@ BEGIN_TEST(testWeakMap_keyDelegates)
 
 static void DelegateObjectMoved(JSObject *obj, const JSObject *old)
 {
+    if (!keyDelegate)
+        return;  // Object got moved before we set keyDelegate to point to it.
+
     MOZ_RELEASE_ASSERT(keyDelegate == old);
     keyDelegate = obj;
 }
@@ -234,12 +237,6 @@ JSObject *newDelegate()
                                 options);
     JS_SetReservedSlot(global, 0, JS::Int32Value(42));
 
-    /*
-     * Ensure the delegate is not in the nursery because for the purpose of this
-     * test we're going to put it in a private slot where it won't get updated.
-     */
-    JS_GC(rt);
-
     return global;
 }
 
diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
index 4b8d01632970..9ec0308b4788 100644
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2098,6 +2098,8 @@ PtrIsInRange(const void *ptr, const void *start, size_t length)
 static bool
 RelocateCell(Zone *zone, TenuredCell *src, AllocKind thingKind, size_t thingSize)
 {
+    JS::AutoSuppressGCAnalysis nogc(zone->runtimeFromMainThread());
+
     // Allocate a new cell.
     MOZ_ASSERT(zone == src->zone());
     void *dstAlloc = zone->arenas.allocateFromFreeList(thingKind, thingSize);
@@ -2373,7 +2375,7 @@ namespace gc {
 
 struct ArenasToUpdate
 {
-    ArenasToUpdate(JSRuntime *rt);
+    explicit ArenasToUpdate(JSRuntime *rt);
     bool done() { return initialized && arena == nullptr; }
     ArenaHeader* next();
     ArenaHeader *getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned max);
@@ -2389,7 +2391,7 @@ struct ArenasToUpdate
 
 bool ArenasToUpdate::shouldProcessKind(unsigned kind)
 {
-    MOZ_ASSERT(kind >= 0 && kind < FINALIZE_LIMIT);
+    MOZ_ASSERT(kind < FINALIZE_LIMIT);
     return
         kind != FINALIZE_FAT_INLINE_STRING &&
         kind != FINALIZE_STRING &&

From cc19a363f60db1f2f069845d331e02ac135824a5 Mon Sep 17 00:00:00 2001
From: Jon Coppeard 
Date: Fri, 16 Jan 2015 14:34:32 +0000
Subject: [PATCH 025/133] Bug 650161 - Enable compacting GC on GC_SHRINK
 collections r=terrence r=glandium

---
 js/src/configure.in                | 12 ------------
 js/src/gc/GCInternals.h            |  2 --
 js/src/gc/GCRuntime.h              | 15 ---------------
 js/src/gc/Heap.h                   |  2 --
 js/src/gc/Marking.cpp              | 16 +---------------
 js/src/js-config.h.in              |  3 ---
 js/src/jsapi-tests/testWeakMap.cpp |  6 ------
 js/src/jscompartment.cpp           |  4 ----
 js/src/jscompartment.h             |  2 --
 js/src/jsgc.cpp                    | 29 ++---------------------------
 js/src/jsgc.h                      | 11 -----------
 js/src/jsinfer.cpp                 |  4 ----
 js/src/jsinfer.h                   |  5 -----
 js/src/jspropertytree.cpp          |  4 ----
 js/src/jspubtd.h                   |  3 +--
 js/src/vm/Shape-inl.h              |  2 --
 js/src/vm/Shape.cpp                |  4 ----
 js/src/vm/Shape.h                  |  8 --------
 18 files changed, 4 insertions(+), 128 deletions(-)

diff --git a/js/src/configure.in b/js/src/configure.in
index a7c981b5e611..23b455d1ebb3 100644
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3054,18 +3054,6 @@ MOZ_ARG_WITH_STRING(wrap-malloc,
 [  --with-wrap-malloc=DIR  Location of malloc wrapper library],
     WRAP_LDFLAGS="${WRAP_LDFLAGS} $withval")
 
-dnl ========================================================
-dnl = Use compacting GC
-dnl ========================================================
-dnl Compact the heap by moving GC things when doing a shrinking colletion.
-MOZ_ARG_ENABLE_BOOL(gccompacting,
-[  --enable-gccompacting   Compact the heap by moving GC things],
-    JSGC_COMPACTING=1,
-    JSGC_COMPACTING= )
-if test -n "$JSGC_COMPACTING"; then
-    AC_DEFINE(JSGC_COMPACTING)
-fi
-
 dnl ========================================================
 dnl = Use a smaller chunk size for GC chunks
 dnl ========================================================
diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h
index 0a718a9ac32b..15b85f26dae4 100644
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -143,7 +143,6 @@ void
 CheckHashTablesAfterMovingGC(JSRuntime *rt);
 #endif
 
-#ifdef JSGC_COMPACTING
 struct MovingTracer : JSTracer {
     explicit MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapKeysValues) {}
 
@@ -152,7 +151,6 @@ struct MovingTracer : JSTracer {
         return trc->callback == Visit;
     }
 };
-#endif
 
 } /* namespace gc */
 } /* namespace js */
diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h
index 65916c2f7c4f..67d8c2758f6d 100644
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -32,11 +32,8 @@ struct FinalizePhase;
 class MarkingValidator;
 struct AutoPrepareForTracing;
 class AutoTraceSession;
-
-#ifdef JSGC_COMPACTING
 struct ArenasToUpdate;
 struct MovingTracer;
-#endif
 
 class ChunkPool
 {
@@ -295,11 +292,7 @@ class GCRuntime
     bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; }
     bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; }
     bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); }
-#ifdef JSGC_COMPACTING
     bool isHeapCompacting() { return isHeapMajorCollecting() && state() == COMPACT; }
-#else
-    bool isHeapCompacting() { return false; }
-#endif
 
     bool triggerGC(JS::gcreason::Reason reason);
     void maybeAllocTriggerZoneGC(Zone *zone, const AutoLockGC &lock);
@@ -435,11 +428,9 @@ class GCRuntime
     void disableGenerationalGC();
     void enableGenerationalGC();
 
-#ifdef JSGC_COMPACTING
     void disableCompactingGC();
     void enableCompactingGC();
     bool isCompactingGCEnabled();
-#endif
 
     void setGrayRootsTracer(JSTraceDataOp traceOp, void *data);
     bool addBlackRootsTracer(JSTraceDataOp traceOp, void *data);
@@ -600,7 +591,6 @@ class GCRuntime
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
     bool compactPhase(bool lastGC);
-#ifdef JSGC_COMPACTING
     void sweepTypesAfterCompacting(Zone *zone);
     void sweepZoneAfterCompacting(Zone *zone);
     ArenaHeader *relocateArenas();
@@ -612,7 +602,6 @@ class GCRuntime
 #ifdef DEBUG
     void protectRelocatedArenas(ArenaHeader *relocatedList);
     void unprotectRelocatedArenas(ArenaHeader *relocatedList);
-#endif
 #endif
     void finishCollection();
 
@@ -809,13 +798,11 @@ class GCRuntime
      */
     unsigned generationalDisabled;
 
-#ifdef JSGC_COMPACTING
     /*
      * Some code cannot tolerate compacting GC so it can be disabled with this
      * counter.
      */
     unsigned compactingDisabled;
-#endif
 
     /*
      * This is true if we are in the middle of a brain transplant (e.g.,
@@ -916,9 +903,7 @@ class GCRuntime
 
     size_t noGCOrAllocationCheck;
 
-#ifdef JSGC_COMPACTING
     ArenaHeader* relocatedArenasToRelease;
-#endif
 
 #endif
 
diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h
index f18d82cd9325..6e53c16948be 100644
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -642,10 +642,8 @@ struct ArenaHeader
 
     void unmarkAll();
 
-#ifdef JSGC_COMPACTING
     size_t countUsedCells();
     size_t countFreeCells();
-#endif
 };
 static_assert(ArenaZoneOffset == offsetof(ArenaHeader, zone),
               "The hardcoded API zone offset must match the actual offset.");
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
index 94500b5ee718..9801a8bd39ea 100644
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -161,18 +161,14 @@ CheckMarkedThing(JSTracer *trc, T **thingp)
     T *thing = *thingp;
     MOZ_ASSERT(*thingp);
 
-#ifdef JSGC_COMPACTING
     thing = MaybeForwarded(thing);
-#endif
 
     /* This function uses data that's not available in the nursery. */
     if (IsInsideNursery(thing))
         return;
 
-#ifdef JSGC_COMPACTING
     MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc) && !Nursery::IsMinorCollectionTracer(trc),
                   !IsForwarded(*thingp));
-#endif
 
     /*
      * Permanent atoms are not associated with this runtime, but will be ignored
@@ -184,13 +180,8 @@ CheckMarkedThing(JSTracer *trc, T **thingp)
     Zone *zone = thing->zoneFromAnyThread();
     JSRuntime *rt = trc->runtime();
 
-#ifdef JSGC_COMPACTING
     MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessZone(zone));
     MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessRuntime(rt));
-#else
-    MOZ_ASSERT(CurrentThreadCanAccessZone(zone));
-    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
-#endif
 
     MOZ_ASSERT(zone->runtimeFromAnyThread() == trc->runtime());
     MOZ_ASSERT(trc->hasTracingDetails());
@@ -437,10 +428,8 @@ IsMarkedFromAnyThread(T **thingp)
     Zone *zone = (*thingp)->asTenured().zoneFromAnyThread();
     if (!zone->isCollectingFromAnyThread() || zone->isGCFinished())
         return true;
-#ifdef JSGC_COMPACTING
     if (zone->isGCCompacting() && IsForwarded(*thingp))
         *thingp = Forwarded(*thingp);
-#endif
     return (*thingp)->asTenured().isMarked();
 }
 
@@ -481,12 +470,10 @@ IsAboutToBeFinalizedFromAnyThread(T **thingp)
             return false;
         return !thing->asTenured().isMarked();
     }
-#ifdef JSGC_COMPACTING
     else if (zone->isGCCompacting() && IsForwarded(thing)) {
         *thingp = Forwarded(thing);
         return false;
     }
-#endif
 
     return false;
 }
@@ -504,11 +491,10 @@ UpdateIfRelocated(JSRuntime *rt, T **thingp)
         return *thingp;
     }
 
-#ifdef JSGC_COMPACTING
     Zone *zone = (*thingp)->zone();
     if (zone->isGCCompacting() && IsForwarded(*thingp))
         *thingp = Forwarded(*thingp);
-#endif
+
     return *thingp;
 }
 
diff --git a/js/src/js-config.h.in b/js/src/js-config.h.in
index b7a7e1a9f3cd..e7e3e1ed2562 100644
--- a/js/src/js-config.h.in
+++ b/js/src/js-config.h.in
@@ -31,9 +31,6 @@
 /* Define to 1 if SpiderMonkey should use small chunks. */
 #undef JS_GC_SMALL_CHUNK_SIZE
 
-/* Define to 1 if SpiderMonkey should use Compacting GC. */
-#undef JSGC_COMPACTING
-
 /* Define to 1 if the  header is present and
    useable.  See jscpucfg.h.  */
 #undef JS_HAVE_ENDIAN_H
diff --git a/js/src/jsapi-tests/testWeakMap.cpp b/js/src/jsapi-tests/testWeakMap.cpp
index a337bcc3ec63..b49967e9cbf1 100644
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -66,10 +66,6 @@ checkSize(JS::HandleObject map, uint32_t expected)
 }
 END_TEST(testWeakMap_basicOperations)
 
-// TODO: this test stores object pointers in a private slot which is not marked
-// and so doesn't work with compacting GC.
-#ifndef JSGC_COMPACTING
-
 BEGIN_TEST(testWeakMap_keyDelegates)
 {
     JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
@@ -253,5 +249,3 @@ checkSize(JS::HandleObject map, uint32_t expected)
     return true;
 }
 END_TEST(testWeakMap_keyDelegates)
-
-#endif
diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp
index b267be3d0ca2..d579615db44c 100644
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -649,8 +649,6 @@ JSCompartment::sweepCrossCompartmentWrappers()
     }
 }
 
-#ifdef JSGC_COMPACTING
-
 void JSCompartment::fixupAfterMovingGC()
 {
     fixupGlobal();
@@ -667,8 +665,6 @@ JSCompartment::fixupGlobal()
         global_.set(MaybeForwarded(global));
 }
 
-#endif // JSGC_COMPACTING
-
 void
 JSCompartment::purge()
 {
diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h
index c6968cd45e22..732c445859f9 100644
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -398,12 +398,10 @@ struct JSCompartment
     void purge();
     void clearTables();
 
-#ifdef JSGC_COMPACTING
     void fixupInitialShapeTable();
     void fixupNewTypeObjectTable(js::types::NewTypeObjectTable &table);
     void fixupAfterMovingGC();
     void fixupGlobal();
-#endif
 
     bool hasObjectMetadataCallback() const { return objectMetadataCallback; }
     void setObjectMetadataCallback(js::ObjectMetadataCallback callback);
diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
index 9ec0308b4788..eb7eca3ec7d0 100644
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1114,9 +1114,7 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     sliceBudget(SliceBudget::Unlimited),
     incrementalAllowed(true),
     generationalDisabled(0),
-#ifdef JSGC_COMPACTING
     compactingDisabled(0),
-#endif
     manipulatingDeadZones(false),
     objectsMarkedInDeadZones(0),
     poked(false),
@@ -1136,9 +1134,7 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
 #ifdef DEBUG
     inUnsafeRegion(0),
     noGCOrAllocationCheck(0),
-#ifdef JSGC_COMPACTING
     relocatedArenasToRelease(nullptr),
-#endif
 #endif
     lock(nullptr),
     lockOwner(nullptr),
@@ -1932,15 +1928,9 @@ ArenaLists::allocateFromArenaInner(JS::Zone *zone, ArenaHeader *aheader, AllocKi
 bool
 GCRuntime::shouldCompact()
 {
-#ifdef JSGC_COMPACTING
     return invocationKind == GC_SHRINK && isCompactingGCEnabled();
-#else
-    return false;
-#endif
 }
 
-#ifdef JSGC_COMPACTING
-
 void
 GCRuntime::disableCompactingGC()
 {
@@ -2699,12 +2689,10 @@ GCRuntime::releaseRelocatedArenasWithoutUnlocking(ArenaHeader *relocatedList, co
     }
 }
 
-#endif // JSGC_COMPACTING
-
 void
 GCRuntime::releaseHeldRelocatedArenas()
 {
-#if defined(JSGC_COMPACTING) && defined(DEBUG)
+#ifdef DEBUG
     // In debug mode we don't release relocated arenas straight away.  Instead
     // we protect them and hold onto them until the next GC sweep phase to catch
     // any pointers to them that didn't get forwarded.
@@ -5467,9 +5455,6 @@ GCRuntime::endSweepPhase(bool lastGC)
 bool
 GCRuntime::compactPhase(bool lastGC)
 {
-#ifndef JSGC_COMPACTING
-    MOZ_CRASH();
-#else
     gcstats::AutoPhase ap(stats, gcstats::PHASE_COMPACT);
 
     if (isIncremental) {
@@ -5534,8 +5519,6 @@ GCRuntime::compactPhase(bool lastGC)
         }
     }
 #endif
-
-#endif // JSGC_COMPACTING
     return true;
 }
 
@@ -5695,7 +5678,6 @@ GCRuntime::resetIncrementalGC(const char *reason)
         break;
       }
 
-#ifdef JSGC_COMPACTING
       case COMPACT: {
         {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
@@ -5711,7 +5693,6 @@ GCRuntime::resetIncrementalGC(const char *reason)
         invocationKind = oldInvocationKind;
         break;
       }
-#endif
 
       default:
         MOZ_CRASH("Invalid incremental GC state");
@@ -6399,7 +6380,7 @@ GCRuntime::onOutOfMallocMemory(const AutoLockGC &lock)
 {
     // Release any relocated arenas we may be holding on to, without releasing
     // the GC lock.
-#if defined(JSGC_COMPACTING) && defined(DEBUG)
+#ifdef DEBUG
     unprotectRelocatedArenas(relocatedArenasToRelease);
     releaseRelocatedArenasWithoutUnlocking(relocatedArenasToRelease, lock);
     relocatedArenasToRelease = nullptr;
@@ -7120,19 +7101,13 @@ JS::IsIncrementalGCEnabled(JSRuntime *rt)
 JS_PUBLIC_API(void)
 JS::DisableCompactingGC(JSRuntime *rt)
 {
-#ifdef JSGC_COMPACTING
     rt->gc.disableCompactingGC();
-#endif
 }
 
 JS_PUBLIC_API(bool)
 JS::IsCompactingGCEnabled(JSRuntime *rt)
 {
-#ifdef JSGC_COMPACTING
     return rt->gc.isCompactingGCEnabled();
-#else
-    return false;
-#endif
 }
 
 JS_PUBLIC_API(bool)
diff --git a/js/src/jsgc.h b/js/src/jsgc.h
index 0bd8f37e1982..7cb3c6ed1649 100644
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -454,11 +454,9 @@ class ArenaList {
         return *this;
     }
 
-#ifdef JSGC_COMPACTING
     ArenaHeader *removeRemainingArenas(ArenaHeader **arenap, const AutoLockGC &lock);
     ArenaHeader *pickArenasToRelocate(JSRuntime *runtime);
     ArenaHeader *relocateArenas(ArenaHeader *toRelocate, ArenaHeader *relocated);
-#endif
 };
 
 /*
@@ -785,9 +783,7 @@ class ArenaLists
         MOZ_ASSERT(freeLists[kind].isEmpty());
     }
 
-#ifdef JSGC_COMPACTING
     ArenaHeader *relocateArenas(ArenaHeader *relocatedList);
-#endif
 
     void queueForegroundObjectsForSweep(FreeOp *fop);
     void queueForegroundThingsForSweep(FreeOp *fop);
@@ -1270,9 +1266,7 @@ inline void
 CheckGCThingAfterMovingGC(T *t)
 {
     MOZ_ASSERT_IF(t, !IsInsideNursery(t));
-#ifdef JSGC_COMPACTING
     MOZ_ASSERT_IF(t, !IsForwarded(t));
-#endif
 }
 
 inline void
@@ -1429,16 +1423,11 @@ struct AutoDisableProxyCheck
 
 struct AutoDisableCompactingGC
 {
-#ifdef JSGC_COMPACTING
     explicit AutoDisableCompactingGC(JSRuntime *rt);
     ~AutoDisableCompactingGC();
 
   private:
     gc::GCRuntime &gc;
-#else
-    explicit AutoDisableCompactingGC(JSRuntime *rt) {}
-    ~AutoDisableCompactingGC() {}
-#endif
 };
 
 void
diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp
index 8900558dc1be..0ea1d8d0dfa3 100644
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -4891,8 +4891,6 @@ JSCompartment::sweepNewTypeObjectTable(NewTypeObjectTable &table)
     }
 }
 
-#ifdef JSGC_COMPACTING
-
 void
 JSCompartment::fixupNewTypeObjectTable(NewTypeObjectTable &table)
 {
@@ -4966,8 +4964,6 @@ TypeObject::fixupAfterMovingGC()
     }
 }
 
-#endif // JSGC_COMPACTING
-
 #ifdef JSGC_HASH_TABLE_CHECKS
 
 void
diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h
index 3d10cbae2961..2c42fd432b75 100644
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -925,10 +925,7 @@ class TypeNewScript
 
     void trace(JSTracer *trc);
     void sweep();
-
-#ifdef JSGC_COMPACTING
     void fixupAfterMovingGC();
-#endif
 
     void registerNewObject(PlainObject *res);
     void unregisterNewObject(PlainObject *res);
@@ -1241,9 +1238,7 @@ struct TypeObject : public gc::TenuredCell
         flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT;
     }
 
-#ifdef JSGC_COMPACTING
     void fixupAfterMovingGC();
-#endif
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
diff --git a/js/src/jspropertytree.cpp b/js/src/jspropertytree.cpp
index 9a3d005c4b71..8dcb4b6ebbd4 100644
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -235,8 +235,6 @@ Shape::finalize(FreeOp *fop)
         fop->delete_(kids.toHash());
 }
 
-#ifdef JSGC_COMPACTING
-
 void
 Shape::fixupDictionaryShapeAfterMovingGC()
 {
@@ -322,8 +320,6 @@ Shape::fixupAfterMovingGC()
         fixupShapeTreeAfterMovingGC();
 }
 
-#endif // JSGC_COMPACTING
-
 void
 ShapeGetterSetterRef::mark(JSTracer *trc)
 {
diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h
index be8894f126ea..15e83e6e752e 100644
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -20,8 +20,7 @@
 
 #include "js/TypeDecls.h"
 
-#if (defined(JS_GC_ZEAL)) || \
-    (defined(JSGC_COMPACTING) && defined(DEBUG))
+#if (defined(JS_GC_ZEAL)) || defined(DEBUG)
 # define JSGC_HASH_TABLE_CHECKS
 #endif
 
diff --git a/js/src/vm/Shape-inl.h b/js/src/vm/Shape-inl.h
index a17755fed1ce..0b5c3ebeb807 100644
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -224,14 +224,12 @@ GetShapeAttributes(JSObject *obj, Shape *shape)
     return shape->attributes();
 }
 
-#ifdef JSGC_COMPACTING
 inline void
 BaseShape::fixupAfterMovingGC()
 {
     if (hasTable())
         table().fixupAfterMovingGC();
 }
-#endif
 
 } /* namespace js */
 
diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp
index 70b86fbb4505..5caf797d9287 100644
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -256,7 +256,6 @@ ShapeTable::search(jsid id, bool adding)
     MOZ_CRASH("Shape::search failed to find an expected entry.");
 }
 
-#ifdef JSGC_COMPACTING
 void
 ShapeTable::fixupAfterMovingGC()
 {
@@ -268,7 +267,6 @@ ShapeTable::fixupAfterMovingGC()
             entry.setPreservingCollision(Forwarded(shape));
     }
 }
-#endif
 
 bool
 ShapeTable::change(int log2Delta, ExclusiveContext *cx)
@@ -1693,7 +1691,6 @@ JSCompartment::sweepInitialShapeTable()
     }
 }
 
-#ifdef JSGC_COMPACTING
 void
 JSCompartment::fixupInitialShapeTable()
 {
@@ -1732,7 +1729,6 @@ JSCompartment::fixupInitialShapeTable()
         }
     }
 }
-#endif // JSGC_COMPACTING
 
 void
 AutoRooterGetterSetter::Inner::trace(JSTracer *trc)
diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h
index 37cf4e15fa74..a7416b02673d 100644
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -227,10 +227,8 @@ class ShapeTable {
     bool change(int log2Delta, ExclusiveContext *cx);
     Entry &search(jsid id, bool adding);
 
-#ifdef JSGC_COMPACTING
     /* Update entries whose shapes have been moved */
     void fixupAfterMovingGC();
-#endif
 
   private:
     Entry &getEntry(uint32_t i) const {
@@ -530,9 +528,7 @@ class BaseShape : public gc::TenuredCell
             gc::MarkObject(trc, &metadata, "metadata");
     }
 
-#ifdef JSGC_COMPACTING
     void fixupAfterMovingGC();
-#endif
 
   private:
     static void staticAsserts() {
@@ -1061,9 +1057,7 @@ class Shape : public gc::TenuredCell
     inline Shape *search(ExclusiveContext *cx, jsid id);
     inline Shape *searchLinear(jsid id);
 
-#ifdef JSGC_COMPACTING
     void fixupAfterMovingGC();
-#endif
 
     /* For JIT usage */
     static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
@@ -1071,10 +1065,8 @@ class Shape : public gc::TenuredCell
     static inline uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; }
 
   private:
-#ifdef JSGC_COMPACTING
     void fixupDictionaryShapeAfterMovingGC();
     void fixupShapeTreeAfterMovingGC();
-#endif
 
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));

From 8ebcff838329f2ec96aac5809527d708f822ebb5 Mon Sep 17 00:00:00 2001
From: Sotaro Ikeda 
Date: Fri, 16 Jan 2015 06:56:14 -0800
Subject: [PATCH 026/133] Bug 1121658 - Remove DestroyDecodedStream() from
 MediaDecoder::SetDormantIfNecessary() r=roc

---
 dom/media/MediaDecoder.cpp             | 1 -
 dom/media/MediaDecoderStateMachine.cpp | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp
index dca8ca7089b2..cc0ba7bd49a7 100644
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -131,7 +131,6 @@ void MediaDecoder::SetDormantIfNecessary(bool aDormant)
 
   if(aDormant) {
     // enter dormant state
-    DestroyDecodedStream();
     mDecoderStateMachine->SetDormant(true);
 
     int64_t timeUsecs = 0;
diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp
index ef7d4bbb8239..36a6ccf3ac4a 100644
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1498,6 +1498,7 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant)
     mCurrentSeekTarget.Reset();
     ScheduleStateMachine();
     SetState(DECODER_STATE_DORMANT);
+    StopPlayback();
     mDecoder->GetReentrantMonitor().NotifyAll();
   } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
     mDecodingFrozenAtStateMetadata = true;

From e22cba5e2b3b6e02a0de60ca8ff79df66429cfff Mon Sep 17 00:00:00 2001
From: Ben Kelly 
Date: Fri, 16 Jan 2015 09:59:32 -0500
Subject: [PATCH 027/133] Bug 1122160 Add operator+=() and operator-=() to
 DebugOnly r=froydnj

---
 mfbt/DebugOnly.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/mfbt/DebugOnly.h b/mfbt/DebugOnly.h
index 5d0197b194ed..719707af569b 100644
--- a/mfbt/DebugOnly.h
+++ b/mfbt/DebugOnly.h
@@ -51,6 +51,10 @@ public:
   void operator++(int) { value++; }
   void operator--(int) { value--; }
 
+  // Do not define operator+=() or operator-=() here.  These will coerce via
+  // the implicit cast and built-in ooperators.  Defining explicit methods here
+  // will create ambiguity the compiler can't deal with.
+
   T* operator&() { return &value; }
 
   operator T&() { return value; }
@@ -66,6 +70,8 @@ public:
   DebugOnly& operator=(const T&) { return *this; }
   void operator++(int) { }
   void operator--(int) { }
+  DebugOnly& operator+=(const T&) { return *this; }
+  DebugOnly& operator-=(const T&) { return *this; }
 #endif
 
   /*

From c2452803faf571dee515b0dc525866316388fe3b Mon Sep 17 00:00:00 2001
From: Ryan VanderMeulen 
Date: Fri, 16 Jan 2015 09:59:57 -0500
Subject: [PATCH 028/133] Bug 1111137 - Disable test_user_agent_overrides.html
 on Android due to frequent failures.

---
 netwerk/test/mochitests/mochitest.ini | 1 +
 1 file changed, 1 insertion(+)

diff --git a/netwerk/test/mochitests/mochitest.ini b/netwerk/test/mochitests/mochitest.ini
index 5594ad0d3ab8..ebd580569ce7 100644
--- a/netwerk/test/mochitests/mochitest.ini
+++ b/netwerk/test/mochitests/mochitest.ini
@@ -10,6 +10,7 @@ support-files =
 [test_partially_cached_content.html]
 [test_uri_scheme.html]
 [test_user_agent_overrides.html]
+skip-if = toolkit == 'android' # Bug 1111137
 [test_user_agent_updates.html]
 [test_user_agent_updates_reset.html]
 [test_xhr_method_case.html]

From d76cb5ba6d80702e8dd931a549fddeb8b1f94281 Mon Sep 17 00:00:00 2001
From: Ryan VanderMeulen 
Date: Fri, 16 Jan 2015 09:59:57 -0500
Subject: [PATCH 029/133] Bug 1061675- Disable test_mozaudiochannel.html on
 Android 2.3 due to frequent failures.

---
 dom/media/webaudio/test/mochitest.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dom/media/webaudio/test/mochitest.ini b/dom/media/webaudio/test/mochitest.ini
index 36cd3186dc3a..83b47d29d1aa 100644
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -119,7 +119,7 @@ skip-if = toolkit == 'android' # bug 1056706
 [test_mixingRules.html]
 skip-if = android_version == '10' # bug 1091965
 [test_mozaudiochannel.html]
-skip-if = (toolkit == 'gonk' && !debug)
+skip-if = (toolkit == 'gonk' && !debug) || android_version == '10' # Android: bug 1061675
 [test_nodeToParamConnection.html]
 [test_OfflineAudioContext.html]
 [test_offlineDestinationChannelCountLess.html]

From ff9da77c31d2779c4571a7eeca4d22eecd6974b2 Mon Sep 17 00:00:00 2001
From: Andreas Pehrson 
Date: Fri, 10 Oct 2014 11:34:57 +0200
Subject: [PATCH 030/133] Bug 879717 - Part 1. Delay entering HAVE_CURRENT_DATA
 state until a

From 4c9aef7ec548ee20df2802f5509e457f66c051ef Mon Sep 17 00:00:00 2001
 video frame has been stored in the image container. r=roc
---
 dom/html/HTMLMediaElement.cpp              | 124 +++++++++++++++++++++++------
 dom/html/HTMLMediaElement.h                |   9 +++
 dom/media/test/test_streams_srcObject.html |   2 +
 3 files changed, 112 insertions(+), 23 deletions(-)
---
 dom/html/HTMLMediaElement.cpp              | 118 +++++++++++++++++----
 dom/html/HTMLMediaElement.h                |   9 ++
 dom/media/test/test_streams_srcObject.html |   2 +
 3 files changed, 109 insertions(+), 20 deletions(-)

diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp
index d8e012120a38..9c9e9cba4a67 100644
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -71,6 +71,8 @@
 #include "mozilla/dom/MediaSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaSourceDecoder.h"
+#include "AudioStreamTrack.h"
+#include "VideoStreamTrack.h"
 
 #include "AudioChannelService.h"
 
@@ -671,7 +673,10 @@ void HTMLMediaElement::AbortExistingLoads()
   mHaveQueuedSelectResource = false;
   mSuspendedForPreloadNone = false;
   mDownloadSuspendedByCache = false;
+  mHasAudio = false;
+  mHasVideo = false;
   mSourcePointer = nullptr;
+  mLastNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
 
   mTags = nullptr;
 
@@ -902,6 +907,30 @@ void HTMLMediaElement::NotifyMediaTrackEnabled(MediaTrack* aTrack)
   }
 }
 
+void HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream)
+{
+  if (!mSrcStream || mSrcStream != aStream) {
+    return;
+  }
+
+  bool oldHasVideo = mHasVideo;
+
+  nsAutoTArray,1> audioTracks;
+  mSrcStream->GetAudioTracks(audioTracks);
+  nsAutoTArray,1> videoTracks;
+  mSrcStream->GetVideoTracks(videoTracks);
+
+  mHasAudio = !audioTracks.IsEmpty();
+  mHasVideo = !videoTracks.IsEmpty();
+
+  if (IsVideo() && oldHasVideo != mHasVideo) {
+    // We are a video element and mHasVideo changed so update the screen wakelock
+    NotifyOwnerDocumentActivityChanged();
+  }
+
+  UpdateReadyStateForData(mLastNextFrameStatus);
+}
+
 void HTMLMediaElement::LoadFromSourceChildren()
 {
   NS_ASSERTION(mDelayingLoadEvent,
@@ -1987,6 +2016,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNo
     mCurrentLoadID(0),
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
+    mLastNextFrameStatus(NEXT_FRAME_UNINITIALIZED),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mPreloadAction(PRELOAD_UNDEFINED),
@@ -2823,6 +2853,25 @@ private:
   bool mPendingNotifyOutput;
 };
 
+class HTMLMediaElement::MediaStreamTracksAvailableCallback:
+    public DOMMediaStream::OnTracksAvailableCallback
+{
+public:
+  explicit MediaStreamTracksAvailableCallback(HTMLMediaElement* aElement,
+                                              DOMMediaStream::TrackTypeHints aExpectedTracks = 0):
+      DOMMediaStream::OnTracksAvailableCallback(aExpectedTracks),
+      mElement(aElement)
+    {}
+  virtual void NotifyTracksAvailable(DOMMediaStream* aStream)
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+    mElement->NotifyMediaStreamTracksAvailable(aStream);
+  }
+private:
+  HTMLMediaElement* mElement;
+};
+
 void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
 {
   NS_ASSERTION(!mSrcStream && !mSrcStreamListener, "Should have been ended already");
@@ -2844,22 +2893,26 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
   if (mPausedForInactiveDocumentOrChannel) {
     GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
   }
+
+  mSrcStream->OnTracksAvailable(new MediaStreamTracksAvailableCallback(this, DOMMediaStream::HINT_CONTENTS_AUDIO));
+  mSrcStream->OnTracksAvailable(new MediaStreamTracksAvailableCallback(this, DOMMediaStream::HINT_CONTENTS_VIDEO));
+
+  ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
+
   ChangeDelayLoadStatus(false);
   GetSrcMediaStream()->AddAudioOutput(this);
-  GetSrcMediaStream()->SetAudioOutputVolume(this, float(mMuted ? 0.0 : mVolume));
+  SetVolumeInternal();
   VideoFrameContainer* container = GetVideoFrameContainer();
   if (container) {
     GetSrcMediaStream()->AddVideoOutput(container);
   }
 
+  CheckAutoplayDataReady();
+
   // Note: we must call DisconnectTrackListListeners(...)  before dropping
   // mSrcStream
   mSrcStream->ConstructMediaTracks(AudioTracks(), VideoTracks());
 
-  ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
-  DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
-  DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
-  ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
   // FirstFrameLoaded() will be called when the stream has current data.
 }
 
@@ -2939,6 +2992,11 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
   } else {
     UpdateMediaSize(aInfo->mVideo.mDisplay);
   }
+
+  if (IsVideo() && aInfo->HasVideo()) {
+    // We are a video element playing video so update the screen wakelock
+    NotifyOwnerDocumentActivityChanged();
+  }
 }
 
 void HTMLMediaElement::FirstFrameLoaded()
@@ -3203,18 +3261,47 @@ bool HTMLMediaElement::ShouldCheckAllowOrigin()
 
 void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame)
 {
-  if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
+  mLastNextFrameStatus = aNextFrame;
+
+  if (mDecoder && mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
     // aNextFrame might have a next frame because the decoder can advance
     // on its own thread before MetadataLoaded gets a chance to run.
     // The arrival of more data can't change us out of this readyState.
     return;
   }
 
+  if (mSrcStream && mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
+    if ((!mHasAudio && !mHasVideo) ||
+        (IsVideo() && mHasVideo && mMediaSize == nsIntSize(-1, -1))) {
+      return;
+    }
+
+    // We are playing a stream that has video and a video frame is now set.
+    // This means we have all metadata needed to change ready state.
+    MediaInfo mediaInfo;
+    mediaInfo.mAudio.mHasAudio = mHasAudio;
+    mediaInfo.mVideo.mHasVideo = mHasVideo;
+    if (mHasVideo) {
+      mediaInfo.mVideo.mDisplay = mMediaSize;
+    }
+    MetadataLoaded(&mediaInfo, nsAutoPtr(nullptr));
+  }
+
   if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
     return;
   }
 
+  if (IsVideo() && mHasVideo && !IsPlaybackEnded() &&
+        GetImageContainer() && !GetImageContainer()->HasCurrentImage()) {
+    // Don't advance if we are playing video, but don't have a video frame.
+    // Also, if video became available after advancing to HAVE_CURRENT_DATA
+    // while we are still playing, we need to revert to HAVE_METADATA until
+    // a video frame is available.
+    ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
+    return;
+  }
+
   if (mDownloadSuspendedByCache && mDecoder && !mDecoder->IsEnded()) {
     // The decoder has signaled that the download has been suspended by the
     // media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
@@ -3365,14 +3452,14 @@ void HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState)
 
 bool HTMLMediaElement::CanActivateAutoplay()
 {
-  // For stream inputs, we activate autoplay on HAVE_CURRENT_DATA because
+  // For stream inputs, we activate autoplay on HAVE_NOTHING because
   // this element itself might be blocking the stream from making progress by
   // being paused.
   return !mPausedForInactiveDocumentOrChannel &&
          mAutoplaying &&
          mPaused &&
          ((mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
-          (mSrcStream && mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)) &&
+          mSrcStream) &&
          HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
          mAutoplayEnabled &&
          !IsEditable();
@@ -3401,24 +3488,14 @@ void HTMLMediaElement::CheckAutoplayDataReady()
 
 VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
 {
-  // If we have loaded the metadata, and the size of the video is still
-  // (-1, -1), the media has no video. Don't go a create a video frame
-  // container.
-  if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
-      mMediaSize == nsIntSize(-1, -1)) {
-    return nullptr;
-  }
+  if (mVideoFrameContainer)
+    return mVideoFrameContainer;
 
   // Only video frames need an image container.
   if (!IsVideo()) {
     return nullptr;
   }
 
-  mHasVideo = true;
-
-  if (mVideoFrameContainer)
-    return mVideoFrameContainer;
-
   mVideoFrameContainer =
     new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
 
@@ -3533,6 +3610,7 @@ void HTMLMediaElement::NotifyDecoderPrincipalChanged()
 void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
 {
   mMediaSize = size;
+  UpdateReadyStateForData(mLastNextFrameStatus);
 }
 
 void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h
index 4f5d6e08fbba..61864b31202e 100644
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -281,6 +281,11 @@ public:
 
   void NotifyMediaTrackEnabled(MediaTrack* aTrack);
 
+  /**
+   * Called when tracks become available to the source media stream.
+   */
+  void NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream);
+
   virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
 
   /**
@@ -612,6 +617,7 @@ protected:
   virtual ~HTMLMediaElement();
 
   class MediaLoadListener;
+  class MediaStreamTracksAvailableCallback;
   class StreamListener;
 
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
@@ -1036,6 +1042,9 @@ protected:
   nsMediaNetworkState mNetworkState;
   nsMediaReadyState mReadyState;
 
+  // Last value passed from codec or stream source to UpdateReadyStateForData.
+  NextFrameStatus mLastNextFrameStatus;
+
   enum LoadAlgorithmState {
     // No load algorithm instance is waiting for a source to be added to the
     // media in order to continue loading.
diff --git a/dom/media/test/test_streams_srcObject.html b/dom/media/test/test_streams_srcObject.html
index 446c5466b41a..ed9c5866783e 100644
--- a/dom/media/test/test_streams_srcObject.html
+++ b/dom/media/test/test_streams_srcObject.html
@@ -45,6 +45,8 @@ function doTest() {
     }
     ++step;
   });
+  a.play();
+  b.play();
 }
 
 
From 9098eab06e6c4f887831718163069ebd4bbb9c65 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Mon, 6 Oct 2014 11:22:55 +0200 Subject: [PATCH 031/133] Bug 879717 - Part 2. Test that drawing a video element to canvas From 5be50c1d6131a58d8a06dcaa0dfca8156ef71273 Mon Sep 17 00:00:00 2001 never throws. r=roc --- dom/media/test/mochitest.ini | 1 + dom/media/test/test_bug879717.html | 119 +++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 dom/media/test/test_bug879717.html --- dom/media/test/mochitest.ini | 1 + dom/media/test/test_bug879717.html | 119 +++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 dom/media/test/test_bug879717.html diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index e6a007ba548f..dbac39c6a36b 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -332,6 +332,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439 [test_bug726904.html] [test_bug874897.html] +[test_bug879717.html] [test_bug883173.html] [test_bug895091.html] [test_bug895305.html] diff --git a/dom/media/test/test_bug879717.html b/dom/media/test/test_bug879717.html new file mode 100644 index 000000000000..35e68fb2860f --- /dev/null +++ b/dom/media/test/test_bug879717.html @@ -0,0 +1,119 @@ + + + + Test for bug 879717, check that a video element can be drawn into a canvas at various states of playback + + + + + +
+
+
+ + From 54ac43e9bb3887a4e0055e0adfb025b09cfbdd75 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Fri, 19 Dec 2014 01:25:00 +0100 Subject: [PATCH 032/133] Bug 879717 - Part 3 - Test video dimensions set on loadedmetadata From 7426ccfec0b1bb8b420dddc628361a4833dc52fa Mon Sep 17 00:00:00 2001 event. r=roc --- dom/media/test/manifest.js | 2 +- dom/media/test/mochitest.ini | 1 + dom/media/test/test_video_dimensions.html | 72 +++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 dom/media/test/test_video_dimensions.html --- dom/media/test/manifest.js | 2 +- dom/media/test/mochitest.ini | 1 + dom/media/test/test_video_dimensions.html | 72 +++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 dom/media/test/test_video_dimensions.html diff --git a/dom/media/test/manifest.js b/dom/media/test/manifest.js index ff7ecd793876..01fec953e575 100644 --- a/dom/media/test/manifest.js +++ b/dom/media/test/manifest.js @@ -14,7 +14,7 @@ var gSmallTests = [ { name:"seek.webm", type:"video/webm", width:320, height:240, duration:3.966 }, { name:"vp9.webm", type:"video/webm", width:320, height:240, duration:4 }, { name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 }, - { name:"gizmo.mp4", type:"video/mp4", duration:5.56 }, + { name:"gizmo.mp4", type:"video/mp4", width:560, height:320, duration:5.56 }, { name:"bogus.duh", type:"bogus/duh" } ]; diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index dbac39c6a36b..cb66459ba65d 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -471,6 +471,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439 [test_reset_events_async.html] [test_reset_src.html] +[test_video_dimensions.html] [test_resume.html] skip-if = true # bug 1021673 [test_seek_out_of_range.html] diff --git a/dom/media/test/test_video_dimensions.html b/dom/media/test/test_video_dimensions.html new file mode 100644 index 000000000000..8c7439e825cc --- /dev/null +++ b/dom/media/test/test_video_dimensions.html @@ -0,0 +1,72 @@ + + + + Test that a video element has set video dimensions on loadedmetadata + + + + + +
+
+
+ + From 29529ab5ec955c7859a7c5910b106d34ee44a945 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Fri, 19 Dec 2014 21:43:58 +0100 Subject: [PATCH 033/133] Bug 879717 - Part 4. Clean up From 16c169589b8a0e47ef3c0816e30547ee6e07fa36 Mon Sep 17 00:00:00 2001 test_peerConnection_capturedVideo.html's waiting for loadedmetadata. r=jesup --- .../mochitest/test_peerConnection_capturedVideo.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) --- .../test_peerConnection_capturedVideo.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html b/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html index 9640b943fa95..423cdb2cfc5f 100644 --- a/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html +++ b/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html @@ -19,22 +19,22 @@ visible: true }); - var domLoaded = new Promise(r => addEventListener("DOMContentLoaded", e => r())); - var test; - var stream; - var waitUntil = func => new Promise(resolve => { - var ival = setInterval(() => func() && resolve(clearInterval(ival)), 200); + var metadataLoaded = new Promise(resolve => { + if (v1.readyState < v1.HAVE_METADATA) { + v1.onloadedmetadata = e => resolve(); + return; + } + resolve(); }); runNetworkTest(function() { - test = new PeerConnectionTest(); + var test = new PeerConnectionTest(); test.setOfferOptions({ offerToReceiveVideo: false, offerToReceiveAudio: false }); test.chain.insertAfter("PC_LOCAL_GUM", [["PC_LOCAL_CAPTUREVIDEO", function (test) { - domLoaded - .then(() => waitUntil(() => v1.videoWidth > 0)) // TODO: Bug 1096723 + metadataLoaded .then(function() { - stream = v1.mozCaptureStreamUntilEnded(); + var stream = v1.mozCaptureStreamUntilEnded(); is(stream.getTracks().length, 2, "Captured stream has 2 tracks"); stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream)); test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests From b1a859d05aeaff31e6c9bb7b94c2fdc11f3b6515 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Fri, 16 Jan 2015 10:22:43 -0500 Subject: [PATCH 034/133] Bug 1122160 Follow-up to fix spelling typo in comment. rs=themaid DONTBUILD --- mfbt/DebugOnly.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mfbt/DebugOnly.h b/mfbt/DebugOnly.h index 719707af569b..0243c3f399f6 100644 --- a/mfbt/DebugOnly.h +++ b/mfbt/DebugOnly.h @@ -52,7 +52,7 @@ public: void operator--(int) { value--; } // Do not define operator+=() or operator-=() here. These will coerce via - // the implicit cast and built-in ooperators. Defining explicit methods here + // the implicit cast and built-in operators. Defining explicit methods here // will create ambiguity the compiler can't deal with. T* operator&() { return &value; } From 225eeb4f6165561e0d3fa0a486bb94de63615bbb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 16 Jan 2015 10:32:55 -0500 Subject: [PATCH 035/133] No bug - fix typo r=me DONTBUILD --HG-- extra : amend_source : c2878d38f04cafa8327bacf8e537428252a89923 --- netwerk/base/src/Predictor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netwerk/base/src/Predictor.cpp b/netwerk/base/src/Predictor.cpp index f427f8afceef..d87cae2185e6 100644 --- a/netwerk/base/src/Predictor.cpp +++ b/netwerk/base/src/Predictor.cpp @@ -1265,7 +1265,7 @@ Predictor::Learn(nsIURI *targetURI, nsIURI *sourceURI, argReason.mLearn = reason; // We always open the full uri (general cache) entry first, so we don't gum up - // the works waiting on predictor-only entires to open + // the works waiting on predictor-only entries to open nsRefPtr uriAction = new Predictor::Action(Predictor::Action::IS_FULL_URI, Predictor::Action::DO_LEARN, argReason, targetURI, From e662668d1243c9a27b2395792017f620f468ce6b Mon Sep 17 00:00:00 2001 From: Sotaro Ikeda Date: Fri, 16 Jan 2015 07:56:19 -0800 Subject: [PATCH 036/133] Bug 1110343 - Suppress redundant loadedmetadata event when dormant exit r=cpearce --- dom/media/AbstractMediaDecoder.h | 34 +++++++++------- dom/media/MediaDecoder.cpp | 14 +++++-- dom/media/MediaDecoder.h | 6 ++- dom/media/MediaDecoderStateMachine.cpp | 40 +++++++++++-------- dom/media/MediaDecoderStateMachine.h | 19 +++++---- dom/media/mediasource/SourceBufferDecoder.cpp | 6 ++- dom/media/mediasource/SourceBufferDecoder.h | 4 +- dom/media/omx/MediaOmxCommonDecoder.cpp | 5 ++- dom/media/omx/MediaOmxCommonDecoder.h | 3 +- dom/media/webaudio/BufferDecoder.cpp | 4 +- dom/media/webaudio/BufferDecoder.h | 4 +- 11 files changed, 85 insertions(+), 54 deletions(-) diff --git a/dom/media/AbstractMediaDecoder.h b/dom/media/AbstractMediaDecoder.h index d96b327d1a65..e2523fbad34d 100644 --- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -91,9 +91,9 @@ public: // Return true if the transport layer supports seeking. virtual bool IsMediaSeekable() = 0; - virtual void MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags) = 0; + virtual void MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags, bool aRestoredFromDromant) = 0; virtual void QueueMetadata(int64_t aTime, nsAutoPtr aInfo, nsAutoPtr aTags) = 0; - virtual void FirstFrameLoaded(nsAutoPtr aInfo) = 0; + virtual void FirstFrameLoaded(nsAutoPtr aInfo, bool aRestoredFromDromant) = 0; virtual void RemoveMediaTracks() = 0; @@ -164,15 +164,18 @@ class MetadataContainer protected: MetadataContainer(AbstractMediaDecoder* aDecoder, nsAutoPtr aInfo, - nsAutoPtr aTags) + nsAutoPtr aTags, + bool aRestoredFromDromant) : mDecoder(aDecoder), mInfo(aInfo), - mTags(aTags) + mTags(aTags), + mRestoredFromDromant(aRestoredFromDromant) {} nsRefPtr mDecoder; nsAutoPtr mInfo; nsAutoPtr mTags; + bool mRestoredFromDromant; }; class MetadataEventRunner : public nsRunnable, private MetadataContainer @@ -180,13 +183,14 @@ class MetadataEventRunner : public nsRunnable, private MetadataContainer public: MetadataEventRunner(AbstractMediaDecoder* aDecoder, nsAutoPtr aInfo, - nsAutoPtr aTags) - : MetadataContainer(aDecoder, aInfo, aTags) + nsAutoPtr aTags, + bool aRestoredFromDromant = false) + : MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDromant) {} NS_IMETHOD Run() MOZ_OVERRIDE { - mDecoder->MetadataLoaded(mInfo, mTags); + mDecoder->MetadataLoaded(mInfo, mTags, mRestoredFromDromant); return NS_OK; } }; @@ -195,13 +199,14 @@ class FirstFrameLoadedEventRunner : public nsRunnable, private MetadataContainer { public: FirstFrameLoadedEventRunner(AbstractMediaDecoder* aDecoder, - nsAutoPtr aInfo) - : MetadataContainer(aDecoder, aInfo, nsAutoPtr(nullptr)) + nsAutoPtr aInfo, + bool aRestoredFromDromant = false) + : MetadataContainer(aDecoder, aInfo, nsAutoPtr(nullptr), aRestoredFromDromant) {} NS_IMETHOD Run() MOZ_OVERRIDE { - mDecoder->FirstFrameLoaded(mInfo); + mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDromant); return NS_OK; } }; @@ -211,16 +216,17 @@ class MetadataUpdatedEventRunner : public nsRunnable, private MetadataContainer public: MetadataUpdatedEventRunner(AbstractMediaDecoder* aDecoder, nsAutoPtr aInfo, - nsAutoPtr aTags) - : MetadataContainer(aDecoder, aInfo, aTags) + nsAutoPtr aTags, + bool aRestoredFromDromant = false) + : MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDromant) {} NS_IMETHOD Run() MOZ_OVERRIDE { nsAutoPtr info(new MediaInfo()); *info = *mInfo; - mDecoder->MetadataLoaded(info, mTags); - mDecoder->FirstFrameLoaded(mInfo); + mDecoder->MetadataLoaded(info, mTags, mRestoredFromDromant); + mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDromant); return NS_OK; } }; diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index cc0ba7bd49a7..c4ef1955ca41 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -696,7 +696,8 @@ MediaDecoder::IsExpectingMoreData() } void MediaDecoder::MetadataLoaded(nsAutoPtr aInfo, - nsAutoPtr aTags) + nsAutoPtr aTags, + bool aRestoredFromDromant) { MOZ_ASSERT(NS_IsMainThread()); @@ -726,11 +727,14 @@ void MediaDecoder::MetadataLoaded(nsAutoPtr aInfo, // Make sure the element and the frame (if any) are told about // our new size. Invalidate(); - mOwner->MetadataLoaded(mInfo, nsAutoPtr(aTags.forget())); + if (!aRestoredFromDromant) { + mOwner->MetadataLoaded(mInfo, nsAutoPtr(aTags.forget())); + } } } -void MediaDecoder::FirstFrameLoaded(nsAutoPtr aInfo) +void MediaDecoder::FirstFrameLoaded(nsAutoPtr aInfo, + bool aRestoredFromDromant) { MOZ_ASSERT(NS_IsMainThread()); @@ -746,7 +750,9 @@ void MediaDecoder::FirstFrameLoaded(nsAutoPtr aInfo) if (mOwner) { Invalidate(); - mOwner->FirstFrameLoaded(); + if (!aRestoredFromDromant) { + mOwner->FirstFrameLoaded(); + } } // This can run cache callbacks. diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 2a1408b88e21..d07e12e5d3a0 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -775,11 +775,13 @@ public: // Called when the metadata from the media file has been loaded by the // state machine. Call on the main thread only. virtual void MetadataLoaded(nsAutoPtr aInfo, - nsAutoPtr aTags) MOZ_OVERRIDE; + nsAutoPtr aTags, + bool aRestoredFromDromant) MOZ_OVERRIDE; // Called when the first audio and/or video from the media file has been loaded // by the state machine. Call on the main thread only. - virtual void FirstFrameLoaded(nsAutoPtr aInfo) MOZ_OVERRIDE; + virtual void FirstFrameLoaded(nsAutoPtr aInfo, + bool aRestoredFromDromant) MOZ_OVERRIDE; // Called from MetadataLoaded(). Creates audio tracks and adds them to its // owner's audio track list, and implies to video tracks respectively. diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 36a6ccf3ac4a..0d39ffafd457 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -222,8 +222,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mWaitingForDecoderSeek(false), mCurrentTimeBeforeSeek(0), mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED), - mDecodingFrozenAtStateMetadata(false), - mDecodingFrozenAtStateDecoding(false) + mDecodingFrozenAtStateDecoding(false), + mSentLoadedMetadataEvent(false), + mSentFirstFrameLoadedEvent(false) { MOZ_COUNT_CTOR(MediaDecoderStateMachine); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); @@ -1501,7 +1502,6 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant) StopPlayback(); mDecoder->GetReentrantMonitor().NotifyAll(); } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) { - mDecodingFrozenAtStateMetadata = true; mDecodingFrozenAtStateDecoding = true; ScheduleStateMachine(); mCurrentFrameTime = 0; @@ -2233,8 +2233,20 @@ MediaDecoderStateMachine::EnqueueLoadedMetadataEvent() nsAutoPtr info(new MediaInfo()); *info = mInfo; nsCOMPtr metadataLoadedEvent = - new MetadataEventRunner(mDecoder, info, mMetadataTags); + new MetadataEventRunner(mDecoder, info, mMetadataTags, mSentLoadedMetadataEvent); NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL); + mSentLoadedMetadataEvent = true; +} + +void +MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent() +{ + nsAutoPtr info(new MediaInfo()); + *info = mInfo; + nsCOMPtr event = + new FirstFrameLoadedEventRunner(mDecoder, info, mSentFirstFrameLoadedEvent); + NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); + mSentFirstFrameLoadedEvent = true; } void @@ -2273,7 +2285,10 @@ MediaDecoderStateMachine::DecodeFirstFrame() SetStartTime(0); nsresult res = FinishDecodeFirstFrame(); NS_ENSURE_SUCCESS(res, res); - } else if (mDecodingFrozenAtStateMetadata) { + } else if (mSentFirstFrameLoadedEvent) { + // We're resuming from dormant state, so we don't need to request + // the first samples in order to determine the media start time, + // we have the start time from last time we loaded. SetStartTime(mStartTime); nsresult res = FinishDecodeFirstFrame(); NS_ENSURE_SUCCESS(res, res); @@ -2308,7 +2323,7 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame() return NS_ERROR_FAILURE; } - if (!IsRealTime() && !mDecodingFrozenAtStateMetadata) { + if (!IsRealTime() && !mSentFirstFrameLoadedEvent) { const VideoData* v = VideoQueue().PeekFront(); const AudioData* a = AudioQueue().PeekFront(); SetStartTime(mReader->ComputeStartTime(v, a)); @@ -2327,8 +2342,6 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame() mStartTime, mEndTime, GetDuration(), mDecoder->IsTransportSeekable(), mDecoder->IsMediaSeekable()); - mDecodingFrozenAtStateMetadata = false; - if (HasAudio() && !HasVideo()) { // We're playing audio only. We don't need to worry about slow video // decodes causing audio underruns, so don't buffer so much audio in @@ -2345,20 +2358,15 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame() nsAutoPtr info(new MediaInfo()); *info = mInfo; - nsCOMPtr event; if (!mGotDurationFromMetaData) { // We now have a duration, we can fire the LoadedMetadata and // FirstFrame event. - event = - new MetadataUpdatedEventRunner(mDecoder, - info, - mMetadataTags); + EnqueueLoadedMetadataEvent(); + EnqueueFirstFrameLoadedEvent(); } else { // Inform the element that we've loaded the first frame. - event = - new FirstFrameLoadedEventRunner(mDecoder, info); + EnqueueFirstFrameLoadedEvent(); } - NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); if (mState == DECODER_STATE_DECODING_FIRSTFRAME) { StartDecoding(); diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 280b067a0ab4..458722e544b3 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -628,6 +628,8 @@ protected: // The decoder monitor must be held. void EnqueueLoadedMetadataEvent(); + void EnqueueFirstFrameLoadedEvent(); + // Dispatches a task to the decode task queue to begin decoding content. // This is threadsafe and can be called on any thread. // The decoder monitor must be held. @@ -1123,16 +1125,19 @@ protected: MediaDecoderOwner::NextFrameStatus mLastFrameStatus; - // True if we are back from DECODER_STATE_DORMANT state, and we can skip + // mDecodingFrozenAtStateDecoding: turn on/off at + // SetDormant/Seek,Play. + bool mDecodingFrozenAtStateDecoding; + + // True if we are back from DECODER_STATE_DORMANT state and + // LoadedMetadataEvent was already sent. + bool mSentLoadedMetadataEvent; + // True if we are back from DECODER_STATE_DORMANT state and + // FirstFrameLoadedEvent was already sent, then we can skip // SetStartTime because the mStartTime already set before. Also we don't need // to decode any audio/video since the MediaDecoder will trigger a seek // operation soon. - // mDecodingFrozenAtStateMetadata: turn on/off at - // SetDormant/FinishDecodeMetadata. - // mDecodingFrozenAtStateDecoding: turn on/off at - // SetDormant/Seek,Play. - bool mDecodingFrozenAtStateMetadata; - bool mDecodingFrozenAtStateDecoding; + bool mSentFirstFrameLoadedEvent; }; } // namespace mozilla; diff --git a/dom/media/mediasource/SourceBufferDecoder.cpp b/dom/media/mediasource/SourceBufferDecoder.cpp index 5a727dfaa4da..6c3fdeccebac 100644 --- a/dom/media/mediasource/SourceBufferDecoder.cpp +++ b/dom/media/mediasource/SourceBufferDecoder.cpp @@ -93,13 +93,15 @@ SourceBufferDecoder::IsMediaSeekable() void SourceBufferDecoder::MetadataLoaded(nsAutoPtr aInfo, - nsAutoPtr aTags) + nsAutoPtr aTags, + bool aRestoredFromDromant) { MSE_DEBUG("SourceBufferDecoder(%p)::MetadataLoaded UNIMPLEMENTED", this); } void -SourceBufferDecoder::FirstFrameLoaded(nsAutoPtr aInfo) +SourceBufferDecoder::FirstFrameLoaded(nsAutoPtr aInfo, + bool aRestoredFromDromant) { MSE_DEBUG("SourceBufferDecoder(%p)::FirstFrameLoaded UNIMPLEMENTED", this); } diff --git a/dom/media/mediasource/SourceBufferDecoder.h b/dom/media/mediasource/SourceBufferDecoder.h index 6931298ff522..53fdf8001794 100644 --- a/dom/media/mediasource/SourceBufferDecoder.h +++ b/dom/media/mediasource/SourceBufferDecoder.h @@ -49,8 +49,8 @@ public: virtual SourceBufferResource* GetResource() const MOZ_FINAL MOZ_OVERRIDE; virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE; virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE; - virtual void MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags) MOZ_FINAL MOZ_OVERRIDE; - virtual void FirstFrameLoaded(nsAutoPtr aInfo) MOZ_FINAL MOZ_OVERRIDE; + virtual void MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE; + virtual void FirstFrameLoaded(nsAutoPtr aInfo, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE; virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE; virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE; virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_FINAL MOZ_OVERRIDE; diff --git a/dom/media/omx/MediaOmxCommonDecoder.cpp b/dom/media/omx/MediaOmxCommonDecoder.cpp index c25035591f89..a6b026ae923c 100644 --- a/dom/media/omx/MediaOmxCommonDecoder.cpp +++ b/dom/media/omx/MediaOmxCommonDecoder.cpp @@ -57,10 +57,11 @@ MediaOmxCommonDecoder::CheckDecoderCanOffloadAudio() } void -MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr aInfo) +MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr aInfo, + bool aRestoredFromDromant) { MOZ_ASSERT(NS_IsMainThread()); - MediaDecoder::FirstFrameLoaded(aInfo); + MediaDecoder::FirstFrameLoaded(aInfo, aRestoredFromDromant); ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); if (!CheckDecoderCanOffloadAudio()) { diff --git a/dom/media/omx/MediaOmxCommonDecoder.h b/dom/media/omx/MediaOmxCommonDecoder.h index 637300aae24b..7f965b6872fc 100644 --- a/dom/media/omx/MediaOmxCommonDecoder.h +++ b/dom/media/omx/MediaOmxCommonDecoder.h @@ -23,7 +23,8 @@ class MediaOmxCommonDecoder : public MediaDecoder public: MediaOmxCommonDecoder(); - virtual void FirstFrameLoaded(nsAutoPtr aInfo); + virtual void FirstFrameLoaded(nsAutoPtr aInfo, + bool aRestoredFromDromant); virtual void ChangeState(PlayState aState); virtual void ApplyStateToStateMachine(PlayState aState); virtual void SetVolume(double aVolume); diff --git a/dom/media/webaudio/BufferDecoder.cpp b/dom/media/webaudio/BufferDecoder.cpp index 07639b2609d4..d33e44391713 100644 --- a/dom/media/webaudio/BufferDecoder.cpp +++ b/dom/media/webaudio/BufferDecoder.cpp @@ -140,13 +140,13 @@ BufferDecoder::IsMediaSeekable() } void -BufferDecoder::MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags) +BufferDecoder::MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags, bool aRestoredFromDromant) { // ignore } void -BufferDecoder::FirstFrameLoaded(nsAutoPtr aInfo) +BufferDecoder::FirstFrameLoaded(nsAutoPtr aInfo, bool aRestoredFromDromant) { // ignore } diff --git a/dom/media/webaudio/BufferDecoder.h b/dom/media/webaudio/BufferDecoder.h index 66c27834db4e..f6b6f406c6c0 100644 --- a/dom/media/webaudio/BufferDecoder.h +++ b/dom/media/webaudio/BufferDecoder.h @@ -59,9 +59,9 @@ public: virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE; - virtual void MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags) MOZ_FINAL MOZ_OVERRIDE; + virtual void MetadataLoaded(nsAutoPtr aInfo, nsAutoPtr aTags, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE; virtual void QueueMetadata(int64_t aTime, nsAutoPtr aInfo, nsAutoPtr aTags) MOZ_FINAL MOZ_OVERRIDE; - virtual void FirstFrameLoaded(nsAutoPtr aInfo) MOZ_FINAL MOZ_OVERRIDE; + virtual void FirstFrameLoaded(nsAutoPtr aInfo, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE; virtual void RemoveMediaTracks() MOZ_FINAL MOZ_OVERRIDE; From a097a74c33dd7c76f4ffd5d96dd3d36a52ad33c1 Mon Sep 17 00:00:00 2001 From: Dave Hunt Date: Wed, 14 Jan 2015 02:25:00 -0500 Subject: [PATCH 037/133] Bug 1118697 - Allow server root to be a base URL to an existing webserver. r=ahal --- .../client/marionette/runner/base.py | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/testing/marionette/client/marionette/runner/base.py b/testing/marionette/client/marionette/runner/base.py index cc6dfb001acf..fd1e56fdb528 100644 --- a/testing/marionette/client/marionette/runner/base.py +++ b/testing/marionette/client/marionette/runner/base.py @@ -25,6 +25,10 @@ from moztest.adapters.unit import StructuredTestRunner, StructuredTestResult from moztest.results import TestResultCollection, TestResult, relevant_line import mozversion + +here = os.path.abspath(os.path.dirname(__file__)) + + class MarionetteTest(TestResult): @property @@ -376,7 +380,9 @@ class BaseMarionetteOptions(OptionParser): self.add_option('--server-root', dest='server_root', action='store', - help='sets the web server\'s root directory to the given path') + help='url to a webserver or path to a document root from which content ' + 'resources are served (default: {}).'.format(os.path.join( + os.path.dirname(here), 'www'))) self.add_option('--gecko-log', dest='gecko_log', action='store', @@ -610,19 +616,22 @@ class BaseMarionetteTestRunner(object): self.failures = [] def start_httpd(self, need_external_ip): - host = "127.0.0.1" - if need_external_ip: - host = moznetwork.get_ip() - docroot = self.server_root or os.path.join(os.path.dirname(os.path.dirname(__file__)), 'www') - if not os.path.isdir(docroot): - raise Exception("Server root %s is not a valid path" % docroot) - self.httpd = MozHttpd(host=host, - port=0, - docroot=docroot) - self.httpd.start() - self.marionette.baseurl = 'http://%s:%d/' % (host, self.httpd.httpd.server_port) - self.logger.info('running webserver on %s' % self.marionette.baseurl) - + if self.server_root is None or os.path.isdir(self.server_root): + host = '127.0.0.1' + if need_external_ip: + host = moznetwork.get_ip() + docroot = self.server_root or os.path.join(os.path.dirname(here), 'www') + if not os.path.isdir(docroot): + raise Exception('Server root %s is not a valid path' % docroot) + self.httpd = MozHttpd(host=host, + port=0, + docroot=docroot) + self.httpd.start() + self.marionette.baseurl = 'http://%s:%d/' % (host, self.httpd.httpd.server_port) + self.logger.info('running webserver on %s' % self.marionette.baseurl) + else: + self.marionette.baseurl = self.server_root + self.logger.info('using content from %s' % self.marionette.baseurl) def _build_kwargs(self): kwargs = { From e486771afae4309e88ff54c9f12904ef15cc6f4c Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Fri, 9 Jan 2015 09:41:00 -0500 Subject: [PATCH 038/133] Bug 1119527 - Implement canvas clearHitRegions. r=gw280, r=bz --- dom/canvas/CanvasRenderingContext2D.cpp | 6 ++++++ dom/canvas/CanvasRenderingContext2D.h | 1 + dom/canvas/test/test_hitregion_canvas.html | 18 +++++++++++++++++- dom/webidl/CanvasRenderingContext2D.webidl | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 6d8b064916d6..5c73f3b07b23 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -3230,6 +3230,12 @@ CanvasRenderingContext2D::RemoveHitRegion(const nsAString& id) } } +void +CanvasRenderingContext2D::ClearHitRegions() +{ + mHitRegionsOptions.Clear(); +} + bool CanvasRenderingContext2D::GetHitRegionRect(Element* aElement, nsRect& aRect) { diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index ebaa3f5e6faf..144800df77fc 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -269,6 +269,7 @@ public: void AddHitRegion(const HitRegionOptions& options, mozilla::ErrorResult& error); void RemoveHitRegion(const nsAString& id); + void ClearHitRegions(); void DrawImage(const HTMLImageOrCanvasOrVideoElement& image, double dx, double dy, mozilla::ErrorResult& error) diff --git a/dom/canvas/test/test_hitregion_canvas.html b/dom/canvas/test/test_hitregion_canvas.html index 273aec3dbc1c..4fc5145f3bf8 100644 --- a/dom/canvas/test/test_hitregion_canvas.html +++ b/dom/canvas/test/test_hitregion_canvas.html @@ -33,10 +33,26 @@ function test_hitregions() { ctx.addHitRegion({control: e}); ctx.addHitRegion({id: "a", control: d}); ctx.addHitRegion({id: "a", control: d}); - + ctx.removeHitRegion("a"); ctx.removeHitRegion("a"); ctx.removeHitRegion("b"); + + ctx.clearHitRegions(); + } catch (e) { + _thrown_outer = true; + } + ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception'); + + var _thrown_outer = false; + try { + ctx.rect(10,10,100,100); + ctx.addHitRegion({control: d}); + ctx.addHitRegion({control: e}); + ctx.addHitRegion({id: "a", control: d}); + ctx.addHitRegion({id: "a", control: d}); + + ctx.clearHitRegions(); } catch (e) { _thrown_outer = true; } diff --git a/dom/webidl/CanvasRenderingContext2D.webidl b/dom/webidl/CanvasRenderingContext2D.webidl index 0f24f684a064..6902f1c3684c 100644 --- a/dom/webidl/CanvasRenderingContext2D.webidl +++ b/dom/webidl/CanvasRenderingContext2D.webidl @@ -125,6 +125,7 @@ interface CanvasRenderingContext2D { // hit regions [Pref="canvas.hitregions.enabled", Throws] void addHitRegion(optional HitRegionOptions options); [Pref="canvas.hitregions.enabled"] void removeHitRegion(DOMString id); + [Pref="canvas.hitregions.enabled"] void clearHitRegions(); // pixel manipulation [NewObject, Throws] From f9075a20d00d5db08ce3b5d94e5ad6d355f6e2fa Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Thu, 15 Jan 2015 19:46:00 -0500 Subject: [PATCH 039/133] Bug 1119670 - Implement processing of scope member of web manifest. r=ehsan --- dom/manifest/ManifestProcessor.jsm | 34 +++++++ dom/manifest/test/mochitest.ini | 3 +- .../test/test_ManifestProcessor_scope.html | 90 +++++++++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 dom/manifest/test/test_ManifestProcessor_scope.html diff --git a/dom/manifest/ManifestProcessor.jsm b/dom/manifest/ManifestProcessor.jsm index 7314a71b092d..b6df920d1aa6 100644 --- a/dom/manifest/ManifestProcessor.jsm +++ b/dom/manifest/ManifestProcessor.jsm @@ -135,6 +135,39 @@ this.ManifestProcessor.prototype.process = function({ return (displayModes.has(value)) ? value : defaultDisplayMode; } + function processScopeMember(manifest, manifestURL, docURL, startURL) { + const spec = { + objectName: 'manifest', + object: manifest, + property: 'scope', + expectedType: 'string', + dontTrim: true + }, + value = extractValue(spec); + let scopeURL; + try { + scopeURL = new URL(value, manifestURL); + } catch (e) { + let msg = 'The URL of scope is invalid.'; + issueDeveloperWarning(msg); + return undefined; + } + + if (scopeURL.origin !== docURL.origin) { + let msg = 'Scope needs to be same-origin as Document.'; + issueDeveloperWarning(msg); + return undefined; + } + + //If start URL is not within scope of scope URL: + if (startURL && startURL.origin !== scopeURL.origin || !startURL.pathname.startsWith(scopeURL.pathname)) { + let msg = 'The start URL is outside the scope, so scope is invalid.'; + issueDeveloperWarning(msg); + return undefined; + } + return scopeURL; + } + function processStartURLMember(manifest, manifestURL, docURL) { const obj = { objectName: 'manifest', @@ -319,5 +352,6 @@ this.ManifestProcessor.prototype.process = function({ icons: processIconsMember(manifest, manifestURL), short_name: processShortNameMember(manifest) }; + processedManifest.scope = processScopeMember(manifest, manifestURL, docURL, processedManifest.start_url); return processedManifest; }; \ No newline at end of file diff --git a/dom/manifest/test/mochitest.ini b/dom/manifest/test/mochitest.ini index 41ceadfe73f4..87fe83a9cab1 100644 --- a/dom/manifest/test/mochitest.ini +++ b/dom/manifest/test/mochitest.ini @@ -13,4 +13,5 @@ support-files = [test_ManifestProcessor_JSON.html] [test_ManifestProcessor_name_and_short_name.html] [test_ManifestProcessor_orientation.html] -[test_ManifestProcessor_start_url.html] \ No newline at end of file +[test_ManifestProcessor_start_url.html] +[test_ManifestProcessor_scope.html] \ No newline at end of file diff --git a/dom/manifest/test/test_ManifestProcessor_scope.html b/dom/manifest/test/test_ManifestProcessor_scope.html new file mode 100644 index 000000000000..51badd7daa9c --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_scope.html @@ -0,0 +1,90 @@ + + + + + + Test for Bug 1079453 + + + + + From f0e5ba672cc07ca9f34f51af531a38934903c1e9 Mon Sep 17 00:00:00 2001 From: Tejas Srinivasan Date: Wed, 14 Jan 2015 23:59:00 -0500 Subject: [PATCH 040/133] Bug 1120652 - Remove MainThreadFetchResolver::OnResponseEnd() and provide an empty definition in the parent class FetchDriverObserver. r=nsm --- dom/fetch/Fetch.cpp | 11 ----------- dom/fetch/FetchDriver.h | 3 ++- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index cf0f2753e098..f3f2fa8afe5b 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -136,9 +136,6 @@ public: void OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE; - void - OnResponseEnd() MOZ_OVERRIDE; - private: ~MainThreadFetchResolver(); }; @@ -265,14 +262,6 @@ MainThreadFetchResolver::OnResponseAvailable(InternalResponse* aResponse) mPromise->MaybeResolve(mResponse); } -void -MainThreadFetchResolver::OnResponseEnd() -{ - NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); - AssertIsOnMainThread(); - MOZ_ASSERT(mResponse); -} - MainThreadFetchResolver::~MainThreadFetchResolver() { NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver); diff --git a/dom/fetch/FetchDriver.h b/dom/fetch/FetchDriver.h index 17303d493138..008b359e7327 100644 --- a/dom/fetch/FetchDriver.h +++ b/dom/fetch/FetchDriver.h @@ -31,7 +31,8 @@ class FetchDriverObserver public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchDriverObserver); virtual void OnResponseAvailable(InternalResponse* aResponse) = 0; - virtual void OnResponseEnd() = 0; + virtual void OnResponseEnd() + { }; protected: virtual ~FetchDriverObserver() From a58d13d7ba13f8e8755f871c169de2135ebeaa62 Mon Sep 17 00:00:00 2001 From: Michael Pruett Date: Thu, 15 Jan 2015 18:01:07 -0600 Subject: [PATCH 041/133] Bug 1121202 - Remove unused PL_DHashTableOperate function. r=njn --- dom/plugins/base/nsJSNPRuntime.cpp | 3 +- xpcom/glue/pldhash.cpp | 6 --- xpcom/glue/pldhash.h | 72 ++++++++++++++---------------- 3 files changed, 34 insertions(+), 47 deletions(-) diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index f381c57e48b2..cd08f7dbc809 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -1774,8 +1774,7 @@ NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old) return; } - // The hazard analysis thinks that PL_DHashTableOperate() can GC but this is - // not possible if we pass PL_DHASH_LOOKUP. + // Calling PL_DHashTableLookup() will not result in GC. JS::AutoSuppressGCAnalysis nogc; NPObjWrapperHashEntry *entry = static_cast diff --git a/xpcom/glue/pldhash.cpp b/xpcom/glue/pldhash.cpp index bde970a27c5c..d3e60fdc87bc 100644 --- a/xpcom/glue/pldhash.cpp +++ b/xpcom/glue/pldhash.cpp @@ -635,12 +635,6 @@ PLDHashTable::Operate(const void* aKey, PLDHashOperator aOp) return entry; } -PLDHashEntryHdr* PL_DHASH_FASTCALL -PL_DHashTableOperate(PLDHashTable* aTable, const void* aKey, PLDHashOperator aOp) -{ - return aTable->Operate(aKey, aOp); -} - PLDHashEntryHdr* PL_DHASH_FASTCALL PL_DHashTableLookup(PLDHashTable* aTable, const void* aKey) { diff --git a/xpcom/glue/pldhash.h b/xpcom/glue/pldhash.h index 8a6c2d21c232..8a0bbc710ad4 100644 --- a/xpcom/glue/pldhash.h +++ b/xpcom/glue/pldhash.h @@ -74,13 +74,13 @@ struct PLDHashTableOps; * of multiplying the hash code returned from the hashKey callback (see below) * by PL_DHASH_GOLDEN_RATIO, then constraining the result to avoid the magic 0 * and 1 values. The stored keyHash value is table size invariant, and it is - * maintained automatically by PL_DHashTableOperate -- users should never set - * it, and its only uses should be via the entry macros below. + * maintained automatically -- users should never set it, and its only uses + * should be via the entry macros below. * * However, use PL_DHASH_ENTRY_IS_BUSY for faster liveness testing of entries - * returned by PL_DHashTableOperate, as PL_DHashTableOperate never returns a - * non-live, busy (i.e., removed) entry pointer to its caller. See below for - * more details on PL_DHashTableOperate's calling rules. + * returned by PL_DHashTableLookup and PL_DHashTableAdd, as these functions + * never return a non-live, busy (i.e., removed) entry pointer to its caller. + * See below for more details on these functions. */ struct PLDHashEntryHdr { @@ -133,8 +133,8 @@ typedef enum PLDHashOperator * that were enumerated so far. Return the total number of live entries when * enumeration completes normally. * - * If etor calls PL_DHashTableOperate on table with op != PL_DHASH_LOOKUP, it - * must return PL_DHASH_STOP; otherwise undefined behavior results. + * If etor calls PL_DHashTableAdd or PL_DHashTableRemove on table, it must + * return PL_DHASH_STOP; otherwise undefined behavior results. * * If any enumerator returns PL_DHASH_REMOVE, aTable->mEntryStore may be shrunk * or compressed after enumeration, but before PL_DHashTableEnumerate returns. @@ -143,8 +143,8 @@ typedef enum PLDHashOperator * aside, e.g., to avoid copying live entries into an array of the entry type. * Copying entry pointers is cheaper, and safe so long as the caller of such a * "stable" Enumerate doesn't use the set-aside pointers after any call either - * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might - * grow or shrink mEntryStore. + * to PL_DHashTableAdd or PL_DHashTableRemove, or to an "unstable" form of + * Enumerate, which might grow or shrink mEntryStore. * * If your enumerator wants to remove certain entries, but set aside pointers * to other entries that it retains, it can use PL_DHashTableRawRemove on the @@ -334,10 +334,9 @@ typedef void (*PLDHashClearEntry)(PLDHashTable* aTable, /* * Initialize a new entry, apart from keyHash. This function is called when - * PL_DHashTableOperate's PL_DHASH_ADD case finds no existing entry for the - * given key, and must add a new one. At that point, aEntry->keyHash is not - * set yet, to avoid claiming the last free entry in a severely overloaded - * table. + * PL_DHashTableAdd finds no existing entry for the given key, and must add a + * new one. At that point, aEntry->keyHash is not set yet, to avoid claiming + * the last free entry in a severely overloaded table. */ typedef bool (*PLDHashInitEntry)(PLDHashTable* aTable, PLDHashEntryHdr* aEntry, const void* aKey); @@ -355,13 +354,13 @@ typedef bool (*PLDHashInitEntry)(PLDHashTable* aTable, PLDHashEntryHdr* aEntry, * clearEntry Run dtor on entry. * * Note the reason why initEntry is optional: the default hooks (stubs) clear - * entry storage: On successful PL_DHashTableOperate(tbl, key, PL_DHASH_ADD), - * the returned entry pointer addresses an entry struct whose keyHash member - * has been set non-zero, but all other entry members are still clear (null). - * PL_DHASH_ADD callers can test such members to see whether the entry was - * newly created by the PL_DHASH_ADD call that just succeeded. If placement - * new or similar initialization is required, define an initEntry hook. Of - * course, the clearEntry hook must zero or null appropriately. + * entry storage: On successful PL_DHashTableAdd(tbl, key), the returned entry + * pointer addresses an entry struct whose keyHash member has been set + * non-zero, but all other entry members are still clear (null). + * PL_DHashTableAdd callers can test such members to see whether the entry was + * newly created by the PL_DHashTableAdd call that just succeeded. If + * placement new or similar initialization is required, define an initEntry + * hook. Of course, the clearEntry hook must zero or null appropriately. * * XXX assumes 0 is null for pointer types. */ @@ -462,14 +461,18 @@ void PL_DHashTableFinish(PLDHashTable* aTable); /* * To lookup a key in table, call: * - * entry = PL_DHashTableOperate(table, key, PL_DHASH_LOOKUP); + * entry = PL_DHashTableLookup(table, key); * * If PL_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies * entry. If PL_DHASH_ENTRY_IS_FREE(entry) is true, key was not found. - * + */ +PLDHashEntryHdr* PL_DHASH_FASTCALL +PL_DHashTableLookup(PLDHashTable* aTable, const void* aKey); + +/* * To add an entry identified by key to table, call: * - * entry = PL_DHashTableOperate(table, key, PL_DHASH_ADD); + * entry = PL_DHashTableAdd(table, key); * * If entry is null upon return, then either the table is severely overloaded, * and memory can't be allocated for entry storage. Or if @@ -480,28 +483,19 @@ void PL_DHashTableFinish(PLDHashTable* aTable); * is true, and it is up to the caller to initialize the key and value parts * of the entry sub-type, if they have not been set already (i.e. if entry was * not already in the table, and if the optional initEntry hook was not used). - * + */ +PLDHashEntryHdr* PL_DHASH_FASTCALL +PL_DHashTableAdd(PLDHashTable* aTable, const void* aKey); + +/* * To remove an entry identified by key from table, call: * - * (void) PL_DHashTableOperate(table, key, PL_DHASH_REMOVE); + * PL_DHashTableRemove(table, key); * * If key's entry is found, it is cleared (via table->ops->clearEntry) and * the entry is marked so that PL_DHASH_ENTRY_IS_FREE(entry). This operation * returns null unconditionally; you should ignore its return value. */ -PLDHashEntryHdr* PL_DHASH_FASTCALL -PL_DHashTableOperate(PLDHashTable* aTable, const void* aKey, - PLDHashOperator aOp); - -/* Look up a key in table. */ -PLDHashEntryHdr* PL_DHASH_FASTCALL -PL_DHashTableLookup(PLDHashTable* aTable, const void* aKey); - -/* Add an entry identified by key to table. */ -PLDHashEntryHdr* PL_DHASH_FASTCALL -PL_DHashTableAdd(PLDHashTable* aTable, const void* aKey); - -/* Remove an entry identified by key from table. */ void PL_DHASH_FASTCALL PL_DHashTableRemove(PLDHashTable* aTable, const void* aKey); @@ -509,7 +503,7 @@ PL_DHashTableRemove(PLDHashTable* aTable, const void* aKey); * Remove an entry already accessed via LOOKUP or ADD. * * NB: this is a "raw" or low-level routine, intended to be used only where - * the inefficiency of a full PL_DHashTableOperate (which rehashes in order + * the inefficiency of a full PL_DHashTableRemove (which rehashes in order * to find the entry given its key) is not tolerable. This function does not * shrink the table if it is underloaded. It does not update mStats #ifdef * PL_DHASHMETER, either. From 78a7dc7fe97e18f8fce232703c3f58e647e192a6 Mon Sep 17 00:00:00 2001 From: Michael Pruett Date: Thu, 15 Jan 2015 18:01:28 -0600 Subject: [PATCH 042/133] Bug 1121202 - Add Lookup, Add, and Remove methods to PLDHashTable. r=njn --- xpcom/glue/pldhash.cpp | 24 +++++++++++++++++++++--- xpcom/glue/pldhash.h | 6 +++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/xpcom/glue/pldhash.cpp b/xpcom/glue/pldhash.cpp index d3e60fdc87bc..cdbca1e519cb 100644 --- a/xpcom/glue/pldhash.cpp +++ b/xpcom/glue/pldhash.cpp @@ -635,22 +635,40 @@ PLDHashTable::Operate(const void* aKey, PLDHashOperator aOp) return entry; } +MOZ_ALWAYS_INLINE PLDHashEntryHdr* +PLDHashTable::Lookup(const void* aKey) +{ + return Operate(aKey, PL_DHASH_LOOKUP); +} + +MOZ_ALWAYS_INLINE PLDHashEntryHdr* +PLDHashTable::Add(const void* aKey) +{ + return Operate(aKey, PL_DHASH_ADD); +} + +MOZ_ALWAYS_INLINE void +PLDHashTable::Remove(const void* aKey) +{ + Operate(aKey, PL_DHASH_REMOVE); +} + PLDHashEntryHdr* PL_DHASH_FASTCALL PL_DHashTableLookup(PLDHashTable* aTable, const void* aKey) { - return aTable->Operate(aKey, PL_DHASH_LOOKUP); + return aTable->Lookup(aKey); } PLDHashEntryHdr* PL_DHASH_FASTCALL PL_DHashTableAdd(PLDHashTable* aTable, const void* aKey) { - return aTable->Operate(aKey, PL_DHASH_ADD); + return aTable->Add(aKey); } void PL_DHASH_FASTCALL PL_DHashTableRemove(PLDHashTable* aTable, const void* aKey) { - aTable->Operate(aKey, PL_DHASH_REMOVE); + aTable->Remove(aKey); } MOZ_ALWAYS_INLINE void diff --git a/xpcom/glue/pldhash.h b/xpcom/glue/pldhash.h index 8a0bbc710ad4..9041245b85de 100644 --- a/xpcom/glue/pldhash.h +++ b/xpcom/glue/pldhash.h @@ -241,7 +241,9 @@ public: void Finish(); - PLDHashEntryHdr* Operate(const void* aKey, PLDHashOperator aOp); + PLDHashEntryHdr* Lookup(const void* aKey); + PLDHashEntryHdr* Add(const void* aKey); + void Remove(const void* aKey); void RawRemove(PLDHashEntryHdr* aEntry); @@ -296,6 +298,8 @@ private: PLDHashEntryHdr* PL_DHASH_FASTCALL FindFreeEntry(PLDHashNumber aKeyHash); + PLDHashEntryHdr* Operate(const void* aKey, PLDHashOperator aOp); + bool ChangeTable(int aDeltaLog2); }; From 5217bbc81f38c9d030771eb3236f45f3d65efb84 Mon Sep 17 00:00:00 2001 From: ziyunfei <446240525@qq.com> Date: Fri, 16 Jan 2015 00:34:00 -0500 Subject: [PATCH 043/133] Bug 1121391 - Update Array.from to match the spec. r=till --- js/src/builtin/Array.js | 97 +++++++++++++----------- js/src/tests/ecma_6/Array/from_basics.js | 3 + js/src/tests/ecma_6/Array/from_errors.js | 7 ++ 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index f4fc89e017bb..c6a07bfe7908 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -707,78 +707,87 @@ function ArrayKeys() { return CreateArrayIterator(this, ITEM_KIND_KEY); } -/* ES6 rev 25 (2014 May 22) 22.1.2.1 */ -function ArrayFrom(arrayLike, mapfn=undefined, thisArg=undefined) { +// ES6 draft rev31 (2015/01/15) 22.1.2.1 Array.from(source[, mapfn[, thisArg]]). +function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { // Step 1. var C = this; // Steps 2-3. - var items = ToObject(arrayLike); - - // Steps 4-5. - var mapping = (mapfn !== undefined); + var mapping = mapfn !== undefined; if (mapping && !IsCallable(mapfn)) ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn)); + var T = thisArg; // All elements defined by this algorithm have the same attrs: var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE; - // Steps 6-8. - var usingIterator = items[std_iterator]; + // Steps 4-5. + var usingIterator = GetMethod(items, std_iterator); + + // Step 6. if (usingIterator !== undefined) { - // Steps 8.a-c. + // Steps 6.a-c. var A = IsConstructor(C) ? new C() : []; - // Steps 8.d-e. - var iterator = callFunction(usingIterator, items); + // Steps 6.d-e. + var iterator = GetIterator(items, usingIterator); - // Step 8.f. + // Step 6.f. var k = 0; - // Steps 8.g.i-vi. + // Step 6.g. // These steps cannot be implemented using a for-of loop. // See . - var next; while (true) { - // Steps 8.g.ii-vi. - next = iterator.next(); + // Steps 6.g.i-iii. + var next = iterator.next(); if (!IsObject(next)) ThrowError(JSMSG_NEXT_RETURNED_PRIMITIVE); - if (next.done) - break; // Substeps of 8.g.iv are implemented below. + + // Step 6.g.iv. + if (next.done) { + A.length = k; + return A; + } + + // Steps 6.g.v-vi. var nextValue = next.value; - // Steps 8.g.vii-viii. + // Steps 6.g.vii-viii. var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue; - // Steps 8.g.ix-xi. + // Steps 6.g.ix-xi. _DefineDataProperty(A, k++, mappedValue, attrs); } - } else { - // Step 9 is an assertion: items is not an Iterator. Testing this is - // literally the very last thing we did, so we don't assert here. - - // Steps 10-12. - // FIXME: Array operations should use ToLength (bug 924058). - var len = ToInteger(items.length); - - // Steps 13-15. - var A = IsConstructor(C) ? new C(len) : NewDenseArray(len); - - // Steps 16-17. - for (var k = 0; k < len; k++) { - // Steps 17.a-c. - var kValue = items[k]; - - // Steps 17.d-e. - var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue; - - // Steps 17.f-g. - _DefineDataProperty(A, k, mappedValue, attrs); - } } - // Steps 8.g.iv.1-3 and 18-20 are the same. - A.length = k; + // Step 7. + assert(usingIterator === undefined, "`items` can't be an Iterable after step 6.g.iv"); + + // Steps 8-9. + var arrayLike = ToObject(items); + + // Steps 10-11. + var len = ToLength(arrayLike.length); + + // Steps 12-14. + var A = IsConstructor(C) ? new C(len) : NewDenseArray(len); + + // Steps 15-16. + for (var k = 0; k < len; k++) { + // Steps 16.a-c. + var kValue = items[k]; + + // Steps 16.d-e. + var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue; + + // Steps 16.f-g. + _DefineDataProperty(A, k, mappedValue, attrs); + } + + // Steps 17-18. + A.length = len; + + // Step 19. return A; } diff --git a/js/src/tests/ecma_6/Array/from_basics.js b/js/src/tests/ecma_6/Array/from_basics.js index c35599b1fa17..623207a41a51 100644 --- a/js/src/tests/ecma_6/Array/from_basics.js +++ b/js/src/tests/ecma_6/Array/from_basics.js @@ -44,5 +44,8 @@ assertDeepEq(Array.from([0, , , ,]), [0, undefined, undefined, undefined]); // Even on non-iterable objects. assertDeepEq(Array.from({length: 4}), [undefined, undefined, undefined, undefined]); +// Array.from should coerce negative lengths to zero. +assertDeepEq(Array.from({length: -1}), []); + if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Array/from_errors.js b/js/src/tests/ecma_6/Array/from_errors.js index 4266cb206dca..98475a802ed3 100644 --- a/js/src/tests/ecma_6/Array/from_errors.js +++ b/js/src/tests/ecma_6/Array/from_errors.js @@ -130,6 +130,13 @@ assertThrowsValue(() => Array.from.call(C, arrayish, () => { throw exc; }), exc) assertEq(log, "lC0"); assertEq(obj instanceof C, true); +// It's a TypeError if the @@iterator property is a primitive (except null and undefined). +for (var primitive of ["foo", 17, Symbol(), true]) { + assertThrowsInstanceOf(() => Array.from({[Symbol.iterator] : primitive}), TypeError); +} +assertDeepEq(Array.from({[Symbol.iterator]: null}), []); +assertDeepEq(Array.from({[Symbol.iterator]: undefined}), []); + // It's a TypeError if the iterator's .next() method returns a primitive. for (var primitive of [undefined, null, 17]) { assertThrowsInstanceOf( From c1b5c21405ff76c46fce7b9ccef6c88f42c3ae23 Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 15 Jan 2015 20:11:20 -0500 Subject: [PATCH 044/133] Bug 1057082 - 1/7 - Add JitActivation fields required for new profiler implementation. r=jandem --- js/src/asmjs/AsmJSValidate.cpp | 31 +++++++++++++++++++++++++++++++ js/src/jit/CompileWrappers.cpp | 6 ++++++ js/src/jit/CompileWrappers.h | 3 +++ js/src/vm/Runtime.cpp | 1 + js/src/vm/Runtime.h | 6 ++++++ js/src/vm/Stack.cpp | 11 ++++++++++- js/src/vm/Stack.h | 33 +++++++++++++++++++++++++++++++++ 7 files changed, 90 insertions(+), 1 deletion(-) diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 8dfb08f1fc63..12833a2c9574 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -8509,22 +8509,39 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit // act.prevJitTop_ = cx->mainThread().jitTop; // act.prevJitJSContext_ = cx->mainThread().jitJSContext; // cx->mainThread().jitJSContext = cx; + // act.prevJitActivation_ = cx->mainThread().jitActivation; + // cx->mainThread().jitActivation = act; // On the ARM store8() uses the secondScratchReg (lr) as a temp. size_t offsetOfActivation = offsetof(JSRuntime, mainThread) + PerThreadData::offsetOfActivation(); size_t offsetOfJitTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitTop); size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitJSContext); + size_t offsetOfJitActivation = offsetof(JSRuntime, mainThread) + + offsetof(PerThreadData, jitActivation); masm.loadAsmJSActivation(reg0); masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3); masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0); masm.loadPtr(Address(reg0, offsetOfActivation), reg1); + + // act.active_ = true; masm.store8(Imm32(1), Address(reg1, JitActivation::offsetOfActiveUint8())); + + // act.prevJitTop_ = cx->mainThread().jitTop; masm.loadPtr(Address(reg0, offsetOfJitTop), reg2); masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitTop())); + + // act.prevJitJSContext_ = cx->mainThread().jitJSContext; masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2); masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext())); + // cx->mainThread().jitJSContext = cx; masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext)); + + // act.prevJitActivation_ = cx->mainThread().jitActivation; + masm.loadPtr(Address(reg0, offsetOfJitActivation), reg2); + masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitActivation())); + // cx->mainThread().jitActivation = act; + masm.storePtr(reg1, Address(reg0, offsetOfJitActivation)); } // 2. Call @@ -8547,19 +8564,33 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit // rt->mainThread.activation()->active_ = false; // rt->mainThread.jitTop = prevJitTop_; // rt->mainThread.jitJSContext = prevJitJSContext_; + // rt->mainThread.jitActivation = prevJitActivation_; // On the ARM store8() uses the secondScratchReg (lr) as a temp. size_t offsetOfActivation = offsetof(JSRuntime, mainThread) + PerThreadData::offsetOfActivation(); size_t offsetOfJitTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitTop); size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitJSContext); + size_t offsetOfJitActivation = offsetof(JSRuntime, mainThread) + + offsetof(PerThreadData, jitActivation); + masm.movePtr(AsmJSImmPtr(AsmJSImm_Runtime), reg0); masm.loadPtr(Address(reg0, offsetOfActivation), reg1); + + // rt->mainThread.activation()->active_ = false; masm.store8(Imm32(0), Address(reg1, JitActivation::offsetOfActiveUint8())); + + // rt->mainThread.jitTop = prevJitTop_; masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitTop()), reg2); masm.storePtr(reg2, Address(reg0, offsetOfJitTop)); + + // rt->mainThread.jitJSContext = prevJitJSContext_; masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitJSContext()), reg2); masm.storePtr(reg2, Address(reg0, offsetOfJitJSContext)); + + // rt->mainThread.jitActivation = prevJitActivation_; + masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitActivation()), reg2); + masm.storePtr(reg2, Address(reg0, offsetOfJitActivation)); } MOZ_ASSERT(masm.framePushed() == framePushed); diff --git a/js/src/jit/CompileWrappers.cpp b/js/src/jit/CompileWrappers.cpp index 645b0dbf6838..a3ffb3144aa2 100644 --- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -40,6 +40,12 @@ CompileRuntime::addressOfJitTop() return &runtime()->mainThread.jitTop; } +const void * +CompileRuntime::addressOfJitActivation() +{ + return &runtime()->mainThread.jitActivation; +} + const void * CompileRuntime::addressOfJitStackLimit() { diff --git a/js/src/jit/CompileWrappers.h b/js/src/jit/CompileWrappers.h index f0e49ddade2a..c83de25196cf 100644 --- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -34,6 +34,9 @@ class CompileRuntime // &mainThread.jitTop const void *addressOfJitTop(); + // &mainThread.jitActivation + const void *addressOfJitActivation(); + // rt->mainThread.jitStackLimit; const void *addressOfJitStackLimit(); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 0b79c6c6be44..d591cd33f02b 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -75,6 +75,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime) runtime_(runtime), jitTop(nullptr), jitJSContext(nullptr), + jitActivation(nullptr), jitStackLimit_(0xbad), #ifdef JS_TRACE_LOGGING traceLogger(nullptr), diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 32f2cbbe8041..9bc7278cab77 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -519,6 +519,12 @@ class PerThreadData : public PerThreadDataFriendFields */ JSContext *jitJSContext; + /* + * Points to the most recent JitActivation pushed on the thread. + * See JitActivation constructor in vm/Stack.cpp + */ + js::jit::JitActivation *jitActivation; + /* See comment for JSRuntime::interrupt_. */ private: mozilla::Atomic jitStackLimit_; diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index d46bf814693f..2643115ee1d9 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1381,15 +1381,20 @@ jit::JitActivation::JitActivation(JSContext *cx, bool active) active_(active), rematerializedFrames_(nullptr), ionRecovery_(cx), - bailoutData_(nullptr) + bailoutData_(nullptr), + lastProfilingFrame_(nullptr), + lastProfilingCallSite_(nullptr) { if (active) { prevJitTop_ = cx->mainThread().jitTop; prevJitJSContext_ = cx->mainThread().jitJSContext; + prevJitActivation_ = cx->mainThread().jitActivation; cx->mainThread().jitJSContext = cx; + cx->mainThread().jitActivation = this; } else { prevJitTop_ = nullptr; prevJitJSContext_ = nullptr; + prevJitActivation_ = nullptr; } } @@ -1398,6 +1403,7 @@ jit::JitActivation::~JitActivation() if (active_) { cx_->perThreadData->jitTop = prevJitTop_; cx_->perThreadData->jitJSContext = prevJitJSContext_; + cx_->perThreadData->jitActivation = prevJitActivation_; } // All reocvered value are taken from activation during the bailout. @@ -1440,10 +1446,13 @@ jit::JitActivation::setActive(JSContext *cx, bool active) if (active) { prevJitTop_ = cx->mainThread().jitTop; prevJitJSContext_ = cx->mainThread().jitJSContext; + prevJitActivation_ = cx->mainThread().jitActivation; cx->mainThread().jitJSContext = cx; + cx->mainThread().jitActivation = this; } else { cx->mainThread().jitTop = prevJitTop_; cx->mainThread().jitJSContext = prevJitJSContext_; + cx->mainThread().jitActivation = prevJitActivation_; } } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 64633c911a10..99211a2f6c65 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -7,6 +7,7 @@ #ifndef vm_Stack_h #define vm_Stack_h +#include "mozilla/Atomics.h" #include "mozilla/MemoryReporting.h" #include "jsfun.h" @@ -1242,6 +1243,7 @@ class BailoutFrameInfo; class JitActivation : public Activation { uint8_t *prevJitTop_; + JitActivation *prevJitActivation_; JSContext *prevJitJSContext_; bool active_; @@ -1271,6 +1273,14 @@ class JitActivation : public Activation // reading it from the stack. BailoutFrameInfo *bailoutData_; + // When profiling is enabled, these fields will be updated to reflect the + // last pushed frame for this activation, and if that frame has been + // left for a call, the native code site of the call. + mozilla::Atomic lastProfilingFrame_; + mozilla::Atomic lastProfilingCallSite_; + static_assert(sizeof(mozilla::Atomic) == sizeof(void *), + "Atomic should have same memory format as underlying type."); + void clearRematerializedFrames(); #ifdef CHECK_OSIPOINT_REGISTERS @@ -1303,6 +1313,9 @@ class JitActivation : public Activation static size_t offsetOfPrevJitJSContext() { return offsetof(JitActivation, prevJitJSContext_); } + static size_t offsetOfPrevJitActivation() { + return offsetof(JitActivation, prevJitActivation_); + } static size_t offsetOfActiveUint8() { MOZ_ASSERT(sizeof(bool) == 1); return offsetof(JitActivation, active_); @@ -1363,6 +1376,26 @@ class JitActivation : public Activation // Unregister the bailout data when the frame is reconstructed. void cleanBailoutData(); + + static size_t offsetOfLastProfilingFrame() { + return offsetof(JitActivation, lastProfilingFrame_); + } + void *lastProfilingFrame() { + return lastProfilingFrame_; + } + void setLastProfilingFrame(void *ptr) { + lastProfilingFrame_ = ptr; + } + + static size_t offsetOfLastProfilingCallSite() { + return offsetof(JitActivation, lastProfilingCallSite_); + } + void *lastProfilingCallSite() { + return lastProfilingCallSite_; + } + void setLastProfilingCallSite(void *ptr) { + lastProfilingCallSite_ = ptr; + } }; // A filtering of the ActivationIterator to only stop at JitActivations. From b3b9045b1180e1c0aca9b5d72f1e7b20d5d6848d Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 15 Jan 2015 20:11:21 -0500 Subject: [PATCH 045/133] Bug 1057082 - 2/7 - Add profiler exit and enter frame instrumentation code. r=jandem --- js/src/jit/CompileWrappers.cpp | 6 + js/src/jit/CompileWrappers.h | 3 + js/src/jit/Ion.cpp | 6 + js/src/jit/JitCompartment.h | 8 + js/src/jit/arm/MacroAssembler-arm.cpp | 15 ++ js/src/jit/arm/MacroAssembler-arm.h | 4 + js/src/jit/arm/Trampoline-arm.cpp | 291 ++++++++++++++++++++++++ js/src/jit/mips/MacroAssembler-mips.cpp | 15 ++ js/src/jit/mips/MacroAssembler-mips.h | 4 + js/src/jit/mips/Trampoline-mips.cpp | 290 +++++++++++++++++++++++ js/src/jit/x64/MacroAssembler-x64.cpp | 15 ++ js/src/jit/x64/MacroAssembler-x64.h | 4 + js/src/jit/x64/Trampoline-x64.cpp | 286 +++++++++++++++++++++++ js/src/jit/x86/MacroAssembler-x86.cpp | 15 ++ js/src/jit/x86/MacroAssembler-x86.h | 4 + js/src/jit/x86/Trampoline-x86.cpp | 289 +++++++++++++++++++++++ js/src/vm/Runtime.h | 2 + 17 files changed, 1257 insertions(+) diff --git a/js/src/jit/CompileWrappers.cpp b/js/src/jit/CompileWrappers.cpp index a3ffb3144aa2..79613533b8b7 100644 --- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -46,6 +46,12 @@ CompileRuntime::addressOfJitActivation() return &runtime()->mainThread.jitActivation; } +const void * +CompileRuntime::addressOfProfilingActivation() +{ + return (const void *) &runtime()->mainThread.profilingActivation_; +} + const void * CompileRuntime::addressOfJitStackLimit() { diff --git a/js/src/jit/CompileWrappers.h b/js/src/jit/CompileWrappers.h index c83de25196cf..ef443327035c 100644 --- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -37,6 +37,9 @@ class CompileRuntime // &mainThread.jitActivation const void *addressOfJitActivation(); + // &mainThread.profilingActivation + const void *addressOfProfilingActivation(); + // rt->mainThread.jitStackLimit; const void *addressOfJitStackLimit(); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 151de8dde236..c6c228329794 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -151,6 +151,7 @@ JitRuntime::JitRuntime() ionAlloc_(nullptr), exceptionTail_(nullptr), bailoutTail_(nullptr), + profilerExitFrameTail_(nullptr), enterJIT_(nullptr), bailoutHandler_(nullptr), argumentsRectifier_(nullptr), @@ -200,6 +201,11 @@ JitRuntime::initialize(JSContext *cx) if (!functionWrappers_ || !functionWrappers_->init()) return false; + JitSpew(JitSpew_Codegen, "# Emitting profiler exit frame tail stub"); + profilerExitFrameTail_ = generateProfilerExitFrameTailStub(cx); + if (!profilerExitFrameTail_) + return false; + JitSpew(JitSpew_Codegen, "# Emitting exception tail stub"); void *handler = JS_FUNC_TO_DATA_PTR(void *, jit::HandleException); diff --git a/js/src/jit/JitCompartment.h b/js/src/jit/JitCompartment.h index 0fa2d647e277..d28234723f25 100644 --- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -158,6 +158,9 @@ class JitRuntime // Shared post-bailout-handler tail. JitCode *bailoutTail_; + // Shared profiler exit frame tail. + JitCode *profilerExitFrameTail_; + // Trampoline for entering JIT code. Contains OSR prologue. JitCode *enterJIT_; @@ -234,6 +237,7 @@ class JitRuntime private: JitCode *generateLazyLinkStub(JSContext *cx); + JitCode *generateProfilerExitFrameTailStub(JSContext *cx); JitCode *generateExceptionTailStub(JSContext *cx, void *handler); JitCode *generateBailoutTailStub(JSContext *cx); JitCode *generateEnterJIT(JSContext *cx, EnterJitType type); @@ -323,6 +327,10 @@ class JitRuntime return bailoutTail_; } + JitCode *getProfilerExitFrameTail() const { + return profilerExitFrameTail_; + } + JitCode *getBailoutTable(const FrameSizeClass &frameClass) const; JitCode *getArgumentsRectifier() const { diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index f8ac9ec7bf2a..ec3ed33cf282 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -5022,3 +5022,18 @@ template void js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op, const Register &value, const BaseIndex &mem, Register temp, Register output); + +void +MacroAssemblerARMCompat::profilerEnterFrame(Register framePtr, Register scratch) +{ + AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation()); + loadPtr(activation, scratch); + storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame())); + storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite())); +} + +void +MacroAssemblerARMCompat::profilerExitFrame() +{ + branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail()); +} diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index d9bf342e3b80..1ee51fcf3ae8 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -1838,6 +1838,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void pushReturnAddress() { push(lr); } + + // Instrumentation for entering and leaving the profiler. + void profilerEnterFrame(Register framePtr, Register scratch); + void profilerExitFrame(); }; typedef MacroAssemblerARMCompat MacroAssemblerSpecific; diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index 4ecf5b8137e6..d43596e72221 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -997,3 +997,294 @@ JitRuntime::generateBailoutTailStub(JSContext *cx) return code; } + +JitCode * +JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx) +{ + MacroAssembler masm; + + Register scratch1 = r5; + Register scratch2 = r6; + Register scratch3 = r7; + Register scratch4 = r8; + + // + // The code generated below expects that the current stack pointer points + // to an Ion or Baseline frame, at the state it would be immediately + // before a ret(). Thus, after this stub's business is done, it executes + // a ret() and returns directly to the caller script, on behalf of the + // callee script that jumped to this code. + // + // Thus the expected stack is: + // + // StackPointer ----+ + // v + // ..., ActualArgc, CalleeToken, Descriptor, ReturnAddr + // MEM-HI MEM-LOW + // + // + // The generated jitcode is responsible for overwriting the + // jitActivation->lastProfilingFrame field with a pointer to the previous + // Ion or Baseline jit-frame that was pushed before this one. It is also + // responsible for overwriting jitActivation->lastProfilingCallSite with + // the return address into that frame. The frame could either be an + // immediate "caller" frame, or it could be a frame in a previous + // JitActivation (if the current frame was entered from C++, and the C++ + // was entered by some caller jit-frame further down the stack). + // + // So this jitcode is responsible for "walking up" the jit stack, finding + // the previous Ion or Baseline JS frame, and storing its address and the + // return address into the appropriate fields on the current jitActivation. + // + // There are a fixed number of different path types that can lead to the + // current frame, which is either a baseline or ion frame: + // + // + // ^ + // | + // ^--- Ion + // | + // ^--- Baseline Stub <---- Baseline + // | + // ^--- Argument Rectifier + // | ^ + // | | + // | ^--- Ion + // | | + // | ^--- Baseline Stub <---- Baseline + // | + // ^--- Entry Frame (From C++) + // + Register actReg = scratch4; + AbsoluteAddress activationAddr(GetJitContext()->runtime->addressOfProfilingActivation()); + masm.loadPtr(activationAddr, actReg); + + Address lastProfilingFrame(actReg, JitActivation::offsetOfLastProfilingFrame()); + Address lastProfilingCallSite(actReg, JitActivation::offsetOfLastProfilingCallSite()); + +#ifdef DEBUG + // Ensure that frame we are exiting is current lastProfilingFrame + { + masm.loadPtr(lastProfilingFrame, scratch1); + Label checkOk; + masm.branchPtr(Assembler::Equal, scratch1, ImmWord(0), &checkOk); + masm.branchPtr(Assembler::Equal, StackPointer, scratch1, &checkOk); + masm.assumeUnreachable( + "Mismatch between stored lastProfilingFrame and current stack pointer."); + masm.bind(&checkOk); + } +#endif + + // Load the frame descriptor into |scratch1|, figure out what to do depending on its type. + masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfDescriptor()), scratch1); + + // Going into the conditionals, we will have: + // FrameDescriptor.size in scratch1 + // FrameDescriptor.type in scratch2 + masm.ma_and(Imm32((1 << FRAMESIZE_SHIFT) - 1), scratch1, scratch2); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1); + + // Handling of each case is dependent on FrameDescriptor.type + Label handle_IonJS; + Label handle_BaselineStub; + Label handle_Rectifier; + Label handle_Entry; + Label end; + + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry); + + masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame."); + + // + // JitFrame_IonJS + // + // Stack layout: + // ... + // Ion-Descriptor + // Prev-FP ---> Ion-ReturnAddr + // ... previous frame data ... |- Descriptor.Size + // ... arguments ... | + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + masm.bind(&handle_IonJS); + { + // |scratch1| contains Descriptor.size + + // returning directly to an IonJS frame. Store return addr to frame + // in lastProfilingCallSite. + masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfReturnAddress()), scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + // Store return frame in lastProfilingFrame. + // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); + masm.ma_add(StackPointer, scratch1, scratch2); + masm.ma_add(scratch2, Imm32(JitFrameLayout::Size()), scratch2); + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + // + // JitFrame_BaselineStub + // + // Look past the stub and store the frame pointer to + // the baselineJS frame prior to it. + // + // Stack layout: + // ... + // BL-Descriptor + // Prev-FP ---> BL-ReturnAddr + // +-----> BL-PrevFramePointer + // | ... BL-FrameData ... + // | BLStub-Descriptor + // | BLStub-ReturnAddr + // | BLStub-StubPointer | + // +------ BLStub-SavedFramePointer |- Descriptor.Size + // ... arguments ... | + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + // We take advantage of the fact that the stub frame saves the frame + // pointer pointing to the baseline frame, so a bunch of calculation can + // be avoided. + // + masm.bind(&handle_BaselineStub); + { + masm.ma_add(StackPointer, scratch1, scratch3); + Address stubFrameReturnAddr(scratch3, + JitFrameLayout::Size() + + BaselineStubFrameLayout::offsetOfReturnAddress()); + masm.loadPtr(stubFrameReturnAddr, scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + Address stubFrameSavedFramePtr(scratch3, + JitFrameLayout::Size() - (2 * sizeof(void *))); + masm.loadPtr(stubFrameSavedFramePtr, scratch2); + masm.addPtr(Imm32(sizeof(void *)), scratch2); // Skip past BL-PrevFramePtr + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + + // + // JitFrame_Rectifier + // + // The rectifier frame can be preceded by either an IonJS or a + // BaselineStub frame. + // + // Stack layout if caller of rectifier was Ion: + // + // Ion-Descriptor + // Ion-ReturnAddr + // ... ion frame data ... |- Rect-Descriptor.Size + // < COMMON LAYOUT > + // + // Stack layout if caller of rectifier was Baseline: + // + // BL-Descriptor + // Prev-FP ---> BL-ReturnAddr + // +-----> BL-SavedFramePointer + // | ... baseline frame data ... + // | BLStub-Descriptor + // | BLStub-ReturnAddr + // | BLStub-StubPointer | + // +------ BLStub-SavedFramePointer |- Rect-Descriptor.Size + // ... args to rectifier ... | + // < COMMON LAYOUT > + // + // Common stack layout: + // + // ActualArgc | + // CalleeToken |- IonRectitiferFrameLayout::Size() + // Rect-Descriptor | + // Rect-ReturnAddr | + // ... rectifier data & args ... |- Descriptor.Size + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + masm.bind(&handle_Rectifier); + { + // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); + masm.ma_add(StackPointer, scratch1, scratch2); + masm.add32(Imm32(JitFrameLayout::Size()), scratch2); + masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfDescriptor()), scratch3); + masm.ma_lsr(Imm32(FRAMESIZE_SHIFT), scratch3, scratch1); + masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch3); + + // Now |scratch1| contains Rect-Descriptor.Size + // and |scratch2| points to Rectifier frame + // and |scratch3| contains Rect-Descriptor.Type + + // Check for either Ion or BaselineStub frame. + Label handle_Rectifier_BaselineStub; + masm.branch32(Assembler::NotEqual, scratch3, Imm32(JitFrame_IonJS), + &handle_Rectifier_BaselineStub); + + // Handle Rectifier <- IonJS + // scratch3 := RectFrame[ReturnAddr] + masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfReturnAddress()), scratch3); + masm.storePtr(scratch3, lastProfilingCallSite); + + // scratch3 := RectFrame + Rect-Descriptor.Size + RectifierFrameLayout::Size() + masm.ma_add(scratch2, scratch1, scratch3); + masm.add32(Imm32(RectifierFrameLayout::Size()), scratch3); + masm.storePtr(scratch3, lastProfilingFrame); + masm.ret(); + + // Handle Rectifier <- BaselineStub <- BaselineJS + masm.bind(&handle_Rectifier_BaselineStub); +#ifdef DEBUG + { + Label checkOk; + masm.branch32(Assembler::Equal, scratch3, Imm32(JitFrame_BaselineStub), &checkOk); + masm.assumeUnreachable("Unrecognized frame preceding baselineStub."); + masm.bind(&checkOk); + } +#endif + masm.ma_add(scratch2, scratch1, scratch3); + Address stubFrameReturnAddr(scratch3, RectifierFrameLayout::Size() + + BaselineStubFrameLayout::offsetOfReturnAddress()); + masm.loadPtr(stubFrameReturnAddr, scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + Address stubFrameSavedFramePtr(scratch3, + RectifierFrameLayout::Size() - (2 * sizeof(void *))); + masm.loadPtr(stubFrameSavedFramePtr, scratch2); + masm.addPtr(Imm32(sizeof(void *)), scratch2); + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + // + // JitFrame_Entry + // + // If at an entry frame, store null into both fields. + // + masm.bind(&handle_Entry); + { + masm.movePtr(ImmPtr(nullptr), scratch1); + masm.storePtr(scratch1, lastProfilingCallSite); + masm.storePtr(scratch1, lastProfilingFrame); + masm.ret(); + } + + Linker linker(masm); + AutoFlushICache afc("ProfilerExitFrameTailStub"); + JitCode *code = linker.newCode(cx, OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "ProfilerExitFrameStub"); +#endif + + return code; +} diff --git a/js/src/jit/mips/MacroAssembler-mips.cpp b/js/src/jit/mips/MacroAssembler-mips.cpp index bdf8b7cdbba7..e7db4ac47ac0 100644 --- a/js/src/jit/mips/MacroAssembler-mips.cpp +++ b/js/src/jit/mips/MacroAssembler-mips.cpp @@ -3669,3 +3669,18 @@ MacroAssemblerMIPSCompat::branchValueIsNurseryObject(Condition cond, ValueOperan bind(&done); } + +void +MacroAssemblerMIPSCompat::profilerEnterFrame(Register reg) +{ + AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation()); + loadPtr(activation, scratch); + storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame())); + storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite())); +} + +void +MacroAssemblerMIPSCompat::profilerExitFrame() +{ + branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail()); +} diff --git a/js/src/jit/mips/MacroAssembler-mips.h b/js/src/jit/mips/MacroAssembler-mips.h index ce9e30a3cedd..8cdd56ae0d6d 100644 --- a/js/src/jit/mips/MacroAssembler-mips.h +++ b/js/src/jit/mips/MacroAssembler-mips.h @@ -1470,6 +1470,10 @@ public: MOZ_ASSERT(Imm16::IsInSignedRange(AsmJSHeapGlobalDataOffset - AsmJSGlobalRegBias)); loadPtr(Address(GlobalReg, AsmJSHeapGlobalDataOffset - AsmJSGlobalRegBias), HeapReg); } + + // Instrumentation for entering and leaving the profiler. + void profilerEnterFrame(Register framePtr, Register scratch); + void profilerExitFrame(); }; typedef MacroAssemblerMIPSCompat MacroAssemblerSpecific; diff --git a/js/src/jit/mips/Trampoline-mips.cpp b/js/src/jit/mips/Trampoline-mips.cpp index d83bfb7ebd1d..6cc82ffb6996 100644 --- a/js/src/jit/mips/Trampoline-mips.cpp +++ b/js/src/jit/mips/Trampoline-mips.cpp @@ -1002,3 +1002,293 @@ JitRuntime::generateBailoutTailStub(JSContext *cx) return code; } +JitCode * +JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx) +{ + MacroAssembler masm; + + Register scratch1 = t0; + Register scratch2 = t1; + Register scratch3 = t2; + Register scratch3 = t3; + + // + // The code generated below expects that the current stack pointer points + // to an Ion or Baseline frame, at the state it would be immediately + // before a ret(). Thus, after this stub's business is done, it executes + // a ret() and returns directly to the caller script, on behalf of the + // callee script that jumped to this code. + // + // Thus the expected stack is: + // + // StackPointer ----+ + // v + // ..., ActualArgc, CalleeToken, Descriptor, ReturnAddr + // MEM-HI MEM-LOW + // + // + // The generated jitcode is responsible for overwriting the + // jitActivation->lastProfilingFrame field with a pointer to the previous + // Ion or Baseline jit-frame that was pushed before this one. It is also + // responsible for overwriting jitActivation->lastProfilingCallSite with + // the return address into that frame. The frame could either be an + // immediate "caller" frame, or it could be a frame in a previous + // JitActivation (if the current frame was entered from C++, and the C++ + // was entered by some caller jit-frame further down the stack). + // + // So this jitcode is responsible for "walking up" the jit stack, finding + // the previous Ion or Baseline JS frame, and storing its address and the + // return address into the appropriate fields on the current jitActivation. + // + // There are a fixed number of different path types that can lead to the + // current frame, which is either a baseline or ion frame: + // + // + // ^ + // | + // ^--- Ion + // | + // ^--- Baseline Stub <---- Baseline + // | + // ^--- Argument Rectifier + // | ^ + // | | + // | ^--- Ion + // | | + // | ^--- Baseline Stub <---- Baseline + // | + // ^--- Entry Frame (From C++) + // + Register actReg = scratch4; + AbsoluteAddress activationAddr(GetJitContext()->runtime->addressOfProfilingActivation()); + masm.loadPtr(activationAddr, actReg); + + Address lastProfilingFrame(actReg, JitActivation::offsetOfLastProfilingFrame()); + Address lastProfilingCallSite(actReg, JitActivation::offsetOfLastProfilingCallSite()); + +#ifdef DEBUG + // Ensure that frame we are exiting is current lastProfilingFrame + { + masm.loadPtr(lastProfilingFrame, scratch1); + Label checkOk; + masm.branchPtr(Assembler::Equal, scratch1, ImmWord(0), &checkOk); + masm.branchPtr(Assembler::Equal, StackPointer, scratch1, &checkOk); + masm.assumeUnreachable( + "Mismatch between stored lastProfilingFrame and current stack pointer."); + masm.bind(&checkOk); + } +#endif + + // Load the frame descriptor into |scratch1|, figure out what to do depending on its type. + masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfDescriptor()), scratch1); + + // Going into the conditionals, we will have: + // FrameDescriptor.size in scratch1 + // FrameDescriptor.type in scratch2 + masm.ma_and(scratch2, scratch1, Imm32((1 << FRAMESIZE_SHIFT) - 1)); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1); + + // Handling of each case is dependent on FrameDescriptor.type + Label handle_IonJS; + Label handle_BaselineStub; + Label handle_Rectifier; + Label handle_Entry; + Label end; + + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry); + + masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame."); + + // + // JitFrame_IonJS + // + // Stack layout: + // ... + // Ion-Descriptor + // Prev-FP ---> Ion-ReturnAddr + // ... previous frame data ... |- Descriptor.Size + // ... arguments ... | + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + masm.bind(&handle_IonJS); + { + // |scratch1| contains Descriptor.size + + // returning directly to an IonJS frame. Store return addr to frame + // in lastProfilingCallSite. + masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfReturnAddress()), scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + // Store return frame in lastProfilingFrame. + // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); + masm.ma_add(scratch2, StackPointer, scratch1); + masm.ma_add(scratch2, scratch2, Imm32(JitFrameLayout::Size())); + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + // + // JitFrame_BaselineStub + // + // Look past the stub and store the frame pointer to + // the baselineJS frame prior to it. + // + // Stack layout: + // ... + // BL-Descriptor + // Prev-FP ---> BL-ReturnAddr + // +-----> BL-PrevFramePointer + // | ... BL-FrameData ... + // | BLStub-Descriptor + // | BLStub-ReturnAddr + // | BLStub-StubPointer | + // +------ BLStub-SavedFramePointer |- Descriptor.Size + // ... arguments ... | + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + // We take advantage of the fact that the stub frame saves the frame + // pointer pointing to the baseline frame, so a bunch of calculation can + // be avoided. + // + masm.bind(&handle_BaselineStub); + { + masm.ma_add(scratch3, StackPointer, scratch1); + Address stubFrameReturnAddr(scratch3, + JitFrameLayout::Size() + + BaselineStubFrameLayout::offsetOfReturnAddress()); + masm.loadPtr(stubFrameReturnAddr, scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + Address stubFrameSavedFramePtr(scratch3, + JitFrameLayout::Size() - (2 * sizeof(void *))); + masm.loadPtr(stubFrameSavedFramePtr, scratch2); + masm.addPtr(Imm32(sizeof(void *)), scratch2); // Skip past BL-PrevFramePtr + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + + // + // JitFrame_Rectifier + // + // The rectifier frame can be preceded by either an IonJS or a + // BaselineStub frame. + // + // Stack layout if caller of rectifier was Ion: + // + // Ion-Descriptor + // Ion-ReturnAddr + // ... ion frame data ... |- Rect-Descriptor.Size + // < COMMON LAYOUT > + // + // Stack layout if caller of rectifier was Baseline: + // + // BL-Descriptor + // Prev-FP ---> BL-ReturnAddr + // +-----> BL-SavedFramePointer + // | ... baseline frame data ... + // | BLStub-Descriptor + // | BLStub-ReturnAddr + // | BLStub-StubPointer | + // +------ BLStub-SavedFramePointer |- Rect-Descriptor.Size + // ... args to rectifier ... | + // < COMMON LAYOUT > + // + // Common stack layout: + // + // ActualArgc | + // CalleeToken |- IonRectitiferFrameLayout::Size() + // Rect-Descriptor | + // Rect-ReturnAddr | + // ... rectifier data & args ... |- Descriptor.Size + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + masm.bind(&handle_Rectifier); + { + // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); + masm.ma_add(scratch2, StackPointer, scratch1); + masm.add32(Imm32(JitFrameLayout::Size()), scratch2); + masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfDescriptor()), scratch3); + masm.ma_lsr(scratch1, scratch3, Imm32(FRAMESIZE_SHIFT)); + masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch3); + + // Now |scratch1| contains Rect-Descriptor.Size + // and |scratch2| points to Rectifier frame + // and |scratch3| contains Rect-Descriptor.Type + + // Check for either Ion or BaselineStub frame. + Label handle_Rectifier_BaselineStub; + masm.branch32(Assembler::NotEqual, scratch3, Imm32(JitFrame_IonJS), + &handle_Rectifier_BaselineStub); + + // Handle Rectifier <- IonJS + // scratch3 := RectFrame[ReturnAddr] + masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfReturnAddress()), scratch3); + masm.storePtr(scratch3, lastProfilingCallSite); + + // scratch3 := RectFrame + Rect-Descriptor.Size + RectifierFrameLayout::Size() + masm.ma_add(scratch3, scratch2, scratch1); + masm.add32(Imm32(RectifierFrameLayout::Size()), scratch3); + masm.storePtr(scratch3, lastProfilingFrame); + masm.ret(); + + // Handle Rectifier <- BaselineStub <- BaselineJS + masm.bind(&handle_Rectifier_BaselineStub); +#ifdef DEBUG + { + Label checkOk; + masm.branch32(Assembler::Equal, scratch3, Imm32(JitFrame_BaselineStub), &checkOk); + masm.assumeUnreachable("Unrecognized frame preceding baselineStub."); + masm.bind(&checkOk); + } +#endif + masm.ma_add(scratch3, scratch2, scratch1); + Address stubFrameReturnAddr(scratch3, RectifierFrameLayout::Size() + + BaselineStubFrameLayout::offsetOfReturnAddress()); + masm.loadPtr(stubFrameReturnAddr, scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + Address stubFrameSavedFramePtr(scratch3, + RectifierFrameLayout::Size() - (2 * sizeof(void *))); + masm.loadPtr(stubFrameSavedFramePtr, scratch2); + masm.addPtr(Imm32(sizeof(void *)), scratch2); + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + // + // JitFrame_Entry + // + // If at an entry frame, store null into both fields. + // + masm.bind(&handle_Entry); + { + masm.movePtr(ImmPtr(nullptr), scratch1); + masm.storePtr(scratch1, lastProfilingCallSite); + masm.storePtr(scratch1, lastProfilingFrame); + masm.ret(); + } + + Linker linker(masm); + AutoFlushICache afc("ProfilerExitFrameTailStub"); + JitCode *code = linker.newCode(cx, OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "ProfilerExitFrameStub"); +#endif + + return code; +} diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 9343fa8642e2..428233dbd0f3 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -519,3 +519,18 @@ MacroAssemblerX64::branchValueIsNurseryObject(Condition cond, ValueOperand value branchPtr(cond == Assembler::Equal ? Assembler::Below : Assembler::AboveOrEqual, ScratchReg, Imm32(nursery.nurserySize()), label); } + +void +MacroAssemblerX64::profilerEnterFrame(Register framePtr, Register scratch) +{ + AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation()); + loadPtr(activation, scratch); + storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame())); + storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite())); +} + +void +MacroAssemblerX64::profilerExitFrame() +{ + jmp(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail()); +} diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index ad6420c0999f..d618d69be256 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -1445,6 +1445,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label *label); void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label *label); + + // Instrumentation for entering and leaving the profiler. + void profilerEnterFrame(Register framePtr, Register scratch); + void profilerExitFrame(); }; typedef MacroAssemblerX64 MacroAssemblerSpecific; diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index d2a506c5461e..a8cf6e64f26e 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -820,3 +820,289 @@ JitRuntime::generateBailoutTailStub(JSContext *cx) return code; } + +JitCode * +JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx) +{ + MacroAssembler masm; + + Register scratch1 = r8; + Register scratch2 = r9; + Register scratch3 = r10; + Register scratch4 = r11; + + // + // The code generated below expects that the current stack pointer points + // to an Ion or Baseline frame, at the state it would be immediately + // before a ret(). Thus, after this stub's business is done, it executes + // a ret() and returns directly to the caller script, on behalf of the + // callee script that jumped to this code. + // + // Thus the expected stack is: + // + // StackPointer ----+ + // v + // ..., ActualArgc, CalleeToken, Descriptor, ReturnAddr + // MEM-HI MEM-LOW + // + // + // The generated jitcode is responsible for overwriting the + // jitActivation->lastProfilingFrame field with a pointer to the previous + // Ion or Baseline jit-frame that was pushed before this one. It is also + // responsible for overwriting jitActivation->lastProfilingCallSite with + // the return address into that frame. The frame could either be an + // immediate "caller" frame, or it could be a frame in a previous + // JitActivation (if the current frame was entered from C++, and the C++ + // was entered by some caller jit-frame further down the stack). + // + // So this jitcode is responsible for "walking up" the jit stack, finding + // the previous Ion or Baseline JS frame, and storing its address and the + // return address into the appropriate fields on the current jitActivation. + // + // There are a fixed number of different path types that can lead to the + // current frame, which is either a baseline or ion frame: + // + // + // ^ + // | + // ^--- Ion + // | + // ^--- Baseline Stub <---- Baseline + // | + // ^--- Argument Rectifier + // | ^ + // | | + // | ^--- Ion + // | | + // | ^--- Baseline Stub <---- Baseline + // | + // ^--- Entry Frame (From C++) + // + Register actReg = scratch4; + AbsoluteAddress activationAddr(GetJitContext()->runtime->addressOfProfilingActivation()); + masm.loadPtr(activationAddr, actReg); + + Address lastProfilingFrame(actReg, JitActivation::offsetOfLastProfilingFrame()); + Address lastProfilingCallSite(actReg, JitActivation::offsetOfLastProfilingCallSite()); + +#ifdef DEBUG + // Ensure that frame we are exiting is current lastProfilingFrame + { + masm.loadPtr(lastProfilingFrame, scratch1); + Label checkOk; + masm.branchPtr(Assembler::Equal, scratch1, ImmWord(0), &checkOk); + masm.branchPtr(Assembler::Equal, StackPointer, scratch1, &checkOk); + masm.assumeUnreachable( + "Mismatch between stored lastProfilingFrame and current stack pointer."); + masm.bind(&checkOk); + } +#endif + + // Load the frame descriptor into |scratch1|, figure out what to do depending on its type. + masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfDescriptor()), scratch1); + + // Going into the conditionals, we will have: + // FrameDescriptor.size in scratch1 + // FrameDescriptor.type in scratch2 + masm.movePtr(scratch1, scratch2); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1); + masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch2); + + // Handling of each case is dependent on FrameDescriptor.type + Label handle_IonJS; + Label handle_BaselineStub; + Label handle_Rectifier; + Label handle_Entry; + Label end; + + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry); + + masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame."); + + // + // JitFrame_IonJS + // + // Stack layout: + // ... + // Ion-Descriptor + // Prev-FP ---> Ion-ReturnAddr + // ... previous frame data ... |- Descriptor.Size + // ... arguments ... | + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + masm.bind(&handle_IonJS); + { + // returning directly to an IonJS frame. Store return addr to frame + // in lastProfilingCallSite. + masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfReturnAddress()), scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + // Store return frame in lastProfilingFrame. + // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); + masm.lea(Operand(StackPointer, scratch1, TimesOne, JitFrameLayout::Size()), scratch2); + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + // + // JitFrame_BaselineStub + // + // Look past the stub and store the frame pointer to + // the baselineJS frame prior to it. + // + // Stack layout: + // ... + // BL-Descriptor + // Prev-FP ---> BL-ReturnAddr + // +-----> BL-PrevFramePointer + // | ... BL-FrameData ... + // | BLStub-Descriptor + // | BLStub-ReturnAddr + // | BLStub-StubPointer | + // +------ BLStub-SavedFramePointer |- Descriptor.Size + // ... arguments ... | + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + // We take advantage of the fact that the stub frame saves the frame + // pointer pointing to the baseline frame, so a bunch of calculation can + // be avoided. + // + masm.bind(&handle_BaselineStub); + { + BaseIndex stubFrameReturnAddr(StackPointer, scratch1, TimesOne, + JitFrameLayout::Size() + + BaselineStubFrameLayout::offsetOfReturnAddress()); + masm.loadPtr(stubFrameReturnAddr, scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + BaseIndex stubFrameSavedFramePtr(StackPointer, scratch1, TimesOne, + JitFrameLayout::Size() - (2 * sizeof(void *))); + masm.loadPtr(stubFrameSavedFramePtr, scratch2); + masm.addPtr(Imm32(sizeof(void *)), scratch2); // Skip past BL-PrevFramePtr + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + + // + // JitFrame_Rectifier + // + // The rectifier frame can be preceded by either an IonJS or a + // BaselineStub frame. + // + // Stack layout if caller of rectifier was Ion: + // + // Ion-Descriptor + // Ion-ReturnAddr + // ... ion frame data ... |- Rect-Descriptor.Size + // < COMMON LAYOUT > + // + // Stack layout if caller of rectifier was Baseline: + // + // BL-Descriptor + // Prev-FP ---> BL-ReturnAddr + // +-----> BL-SavedFramePointer + // | ... baseline frame data ... + // | BLStub-Descriptor + // | BLStub-ReturnAddr + // | BLStub-StubPointer | + // +------ BLStub-SavedFramePointer |- Rect-Descriptor.Size + // ... args to rectifier ... | + // < COMMON LAYOUT > + // + // Common stack layout: + // + // ActualArgc | + // CalleeToken |- IonRectitiferFrameLayout::Size() + // Rect-Descriptor | + // Rect-ReturnAddr | + // ... rectifier data & args ... |- Descriptor.Size + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + masm.bind(&handle_Rectifier); + { + // scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size() + masm.lea(Operand(StackPointer, scratch1, TimesOne, JitFrameLayout::Size()), scratch2); + masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfDescriptor()), scratch3); + masm.movePtr(scratch3, scratch1); + masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch3); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1); + + // Now |scratch1| contains Rect-Descriptor.Size + // and |scratch2| points to Rectifier frame + // and |scratch3| contains Rect-Descriptor.Type + + // Check for either Ion or BaselineStub frame. + Label handle_Rectifier_BaselineStub; + masm.branch32(Assembler::NotEqual, scratch3, Imm32(JitFrame_IonJS), + &handle_Rectifier_BaselineStub); + + // Handle Rectifier <- IonJS + // scratch3 := RectFrame[ReturnAddr] + masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfReturnAddress()), scratch3); + masm.storePtr(scratch3, lastProfilingCallSite); + + // scratch3 := RectFrame + Rect-Descriptor.Size + RectifierFrameLayout::Size() + masm.lea(Operand(scratch2, scratch1, TimesOne, RectifierFrameLayout::Size()), scratch3); + masm.storePtr(scratch3, lastProfilingFrame); + masm.ret(); + + // Handle Rectifier <- BaselineStub <- BaselineJS + masm.bind(&handle_Rectifier_BaselineStub); +#ifdef DEBUG + { + Label checkOk; + masm.branch32(Assembler::Equal, scratch3, Imm32(JitFrame_BaselineStub), &checkOk); + masm.assumeUnreachable("Unrecognized frame preceding baselineStub."); + masm.bind(&checkOk); + } +#endif + BaseIndex stubFrameReturnAddr(scratch2, scratch1, TimesOne, + RectifierFrameLayout::Size() + + BaselineStubFrameLayout::offsetOfReturnAddress()); + masm.loadPtr(stubFrameReturnAddr, scratch3); + masm.storePtr(scratch3, lastProfilingCallSite); + + BaseIndex stubFrameSavedFramePtr(scratch2, scratch1, TimesOne, + RectifierFrameLayout::Size() - (2 * sizeof(void *))); + masm.loadPtr(stubFrameSavedFramePtr, scratch3); + masm.addPtr(Imm32(sizeof(void *)), scratch3); + masm.storePtr(scratch3, lastProfilingFrame); + masm.ret(); + } + + // + // JitFrame_Entry + // + // If at an entry frame, store null into both fields. + // + masm.bind(&handle_Entry); + { + masm.movePtr(ImmPtr(nullptr), scratch1); + masm.storePtr(scratch1, lastProfilingCallSite); + masm.storePtr(scratch1, lastProfilingFrame); + masm.ret(); + } + + Linker linker(masm); + JitCode *code = linker.newCode(cx, OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "ProfilerExitFrameStub"); +#endif + + return code; +} diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index c158c3f9037e..23b4f418fc29 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -514,3 +514,18 @@ MacroAssemblerX86::branchValueIsNurseryObject(Condition cond, ValueOperand value bind(&done); } + +void +MacroAssemblerX86::profilerEnterFrame(Register framePtr, Register scratch) +{ + AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation()); + loadPtr(activation, scratch); + storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame())); + storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite())); +} + +void +MacroAssemblerX86::profilerExitFrame() +{ + jmp(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail()); +} diff --git a/js/src/jit/x86/MacroAssembler-x86.h b/js/src/jit/x86/MacroAssembler-x86.h index 5e3486ae79e1..25be7bef55f2 100644 --- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -1195,6 +1195,10 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label *label); void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label *label); + + // Instrumentation for entering and leaving the profiler. + void profilerEnterFrame(Register framePtr, Register scratch); + void profilerExitFrame(); }; typedef MacroAssemblerX86 MacroAssemblerSpecific; diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index 7f6902822558..af180bca799a 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -858,3 +858,292 @@ JitRuntime::generateBailoutTailStub(JSContext *cx) return code; } + +JitCode * +JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx) +{ + MacroAssembler masm; + + Register scratch1 = eax; + Register scratch2 = ebx; + Register scratch3 = esi; + Register scratch4 = edi; + + // + // The code generated below expects that the current stack pointer points + // to an Ion or Baseline frame, at the state it would be immediately + // before a ret(). Thus, after this stub's business is done, it executes + // a ret() and returns directly to the caller script, on behalf of the + // callee script that jumped to this code. + // + // Thus the expected stack is: + // + // StackPointer ----+ + // v + // ..., ActualArgc, CalleeToken, Descriptor, ReturnAddr + // MEM-HI MEM-LOW + // + // + // The generated jitcode is responsible for overwriting the + // jitActivation->lastProfilingFrame field with a pointer to the previous + // Ion or Baseline jit-frame that was pushed before this one. It is also + // responsible for overwriting jitActivation->lastProfilingCallSite with + // the return address into that frame. The frame could either be an + // immediate "caller" frame, or it could be a frame in a previous + // JitActivation (if the current frame was entered from C++, and the C++ + // was entered by some caller jit-frame further down the stack). + // + // So this jitcode is responsible for "walking up" the jit stack, finding + // the previous Ion or Baseline JS frame, and storing its address and the + // return address into the appropriate fields on the current jitActivation. + // + // There are a fixed number of different path types that can lead to the + // current frame, which is either a baseline or ion frame: + // + // + // ^ + // | + // ^--- Ion + // | + // ^--- Baseline Stub <---- Baseline + // | + // ^--- Argument Rectifier + // | ^ + // | | + // | ^--- Ion + // | | + // | ^--- Baseline Stub <---- Baseline + // | + // ^--- Entry Frame (From C++) + // + Register actReg = scratch4; + AbsoluteAddress activationAddr(GetJitContext()->runtime->addressOfProfilingActivation()); + masm.loadPtr(activationAddr, actReg); + + Address lastProfilingFrame(actReg, JitActivation::offsetOfLastProfilingFrame()); + Address lastProfilingCallSite(actReg, JitActivation::offsetOfLastProfilingCallSite()); + +#ifdef DEBUG + // Ensure that frame we are exiting is current lastProfilingFrame + { + masm.loadPtr(lastProfilingFrame, scratch1); + Label checkOk; + masm.branchPtr(Assembler::Equal, scratch1, ImmWord(0), &checkOk); + masm.branchPtr(Assembler::Equal, StackPointer, scratch1, &checkOk); + masm.assumeUnreachable( + "Mismatch between stored lastProfilingFrame and current stack pointer."); + masm.bind(&checkOk); + } +#endif + + // Load the frame descriptor into |scratch1|, figure out what to do + // depending on its type. + masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfDescriptor()), scratch1); + + // Going into the conditionals, we will have: + // FrameDescriptor.size in scratch1 + // FrameDescriptor.type in scratch2 + masm.movePtr(scratch1, scratch2); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1); + masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch2); + + // Handling of each case is dependent on FrameDescriptor.type + Label handle_IonJS; + Label handle_BaselineStub; + Label handle_Rectifier; + Label handle_Entry; + Label end; + + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier); + masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry); + + masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame."); + + // + // JitFrame_IonJS + // + // Stack layout: + // ... + // Ion-Descriptor + // Prev-FP ---> Ion-ReturnAddr + // ... previous frame data ... |- Descriptor.Size + // ... arguments ... | + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + masm.bind(&handle_IonJS); + { + // |scratch1| contains Descriptor.size + + // returning directly to an IonJS frame. Store return addr to frame + // in lastProfilingCallSite. + masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfReturnAddress()), scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + // Store return frame in lastProfilingFrame. + // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); + masm.lea(Operand(StackPointer, scratch1, TimesOne, JitFrameLayout::Size()), scratch2); + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + // + // JitFrame_BaselineStub + // + // Look past the stub and store the frame pointer to + // the baselineJS frame prior to it. + // + // Stack layout: + // ... + // BL-Descriptor + // Prev-FP ---> BL-ReturnAddr + // +-----> BL-PrevFramePointer + // | ... BL-FrameData ... + // | BLStub-Descriptor + // | BLStub-ReturnAddr + // | BLStub-StubPointer | + // +------ BLStub-SavedFramePointer |- Descriptor.Size + // ... arguments ... | + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + // We take advantage of the fact that the stub frame saves the frame + // pointer pointing to the baseline frame, so a bunch of calculation can + // be avoided. + // + masm.bind(&handle_BaselineStub); + { + BaseIndex stubFrameReturnAddr(StackPointer, scratch1, TimesOne, + JitFrameLayout::Size() + + BaselineStubFrameLayout::offsetOfReturnAddress()); + masm.loadPtr(stubFrameReturnAddr, scratch2); + masm.storePtr(scratch2, lastProfilingCallSite); + + BaseIndex stubFrameSavedFramePtr(StackPointer, scratch1, TimesOne, + JitFrameLayout::Size() - (2 * sizeof(void *))); + masm.loadPtr(stubFrameSavedFramePtr, scratch2); + masm.addPtr(Imm32(sizeof(void *)), scratch2); // Skip past BL-PrevFramePtr + masm.storePtr(scratch2, lastProfilingFrame); + masm.ret(); + } + + + // + // JitFrame_Rectifier + // + // The rectifier frame can be preceded by either an IonJS or a + // BaselineStub frame. + // + // Stack layout if caller of rectifier was Ion: + // + // Ion-Descriptor + // Ion-ReturnAddr + // ... ion frame data ... |- Rect-Descriptor.Size + // < COMMON LAYOUT > + // + // Stack layout if caller of rectifier was Baseline: + // + // BL-Descriptor + // Prev-FP ---> BL-ReturnAddr + // +-----> BL-SavedFramePointer + // | ... baseline frame data ... + // | BLStub-Descriptor + // | BLStub-ReturnAddr + // | BLStub-StubPointer | + // +------ BLStub-SavedFramePointer |- Rect-Descriptor.Size + // ... args to rectifier ... | + // < COMMON LAYOUT > + // + // Common stack layout: + // + // ActualArgc | + // CalleeToken |- IonRectitiferFrameLayout::Size() + // Rect-Descriptor | + // Rect-ReturnAddr | + // ... rectifier data & args ... |- Descriptor.Size + // ActualArgc | + // CalleeToken |- JitFrameLayout::Size() + // Descriptor | + // FP -----> ReturnAddr | + // + masm.bind(&handle_Rectifier); + { + // scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size() + masm.lea(Operand(StackPointer, scratch1, TimesOne, JitFrameLayout::Size()), scratch2); + masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfDescriptor()), scratch3); + masm.movePtr(scratch3, scratch1); + masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch3); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1); + + // Now |scratch1| contains Rect-Descriptor.Size + // and |scratch2| points to Rectifier frame + // and |scratch3| contains Rect-Descriptor.Type + + // Check for either Ion or BaselineStub frame. + Label handle_Rectifier_BaselineStub; + masm.branch32(Assembler::NotEqual, scratch3, Imm32(JitFrame_IonJS), + &handle_Rectifier_BaselineStub); + + // Handle Rectifier <- IonJS + // scratch3 := RectFrame[ReturnAddr] + masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfReturnAddress()), scratch3); + masm.storePtr(scratch3, lastProfilingCallSite); + + // scratch3 := RectFrame + Rect-Descriptor.Size + RectifierFrameLayout::Size() + masm.lea(Operand(scratch2, scratch1, TimesOne, RectifierFrameLayout::Size()), scratch3); + masm.storePtr(scratch3, lastProfilingFrame); + masm.ret(); + + // Handle Rectifier <- BaselineStub <- BaselineJS + masm.bind(&handle_Rectifier_BaselineStub); +#ifdef DEBUG + { + Label checkOk; + masm.branch32(Assembler::Equal, scratch3, Imm32(JitFrame_BaselineStub), &checkOk); + masm.assumeUnreachable("Unrecognized frame preceding baselineStub."); + masm.bind(&checkOk); + } +#endif + BaseIndex stubFrameReturnAddr(scratch2, scratch1, TimesOne, + RectifierFrameLayout::Size() + + BaselineStubFrameLayout::offsetOfReturnAddress()); + masm.loadPtr(stubFrameReturnAddr, scratch3); + masm.storePtr(scratch3, lastProfilingCallSite); + + BaseIndex stubFrameSavedFramePtr(scratch2, scratch1, TimesOne, + RectifierFrameLayout::Size() - (2 * sizeof(void *))); + masm.loadPtr(stubFrameSavedFramePtr, scratch3); + masm.addPtr(Imm32(sizeof(void *)), scratch3); + masm.storePtr(scratch3, lastProfilingFrame); + masm.ret(); + } + + // + // JitFrame_Entry + // + // If at an entry frame, store null into both fields. + // + masm.bind(&handle_Entry); + { + masm.movePtr(ImmPtr(nullptr), scratch1); + masm.storePtr(scratch1, lastProfilingCallSite); + masm.storePtr(scratch1, lastProfilingFrame); + masm.ret(); + } + + Linker linker(masm); + JitCode *code = linker.newCode(cx, OTHER_CODE); + +#ifdef JS_ION_PERF + writePerfSpewerJitCodeProfile(code, "ProfilerExitFrameStub"); +#endif + + return code; +} diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 9bc7278cab77..b6cc12317e4c 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -87,6 +87,7 @@ struct PcScriptCache; class Simulator; class SimulatorRuntime; struct AutoFlushICache; +class CompileRuntime; } /* @@ -551,6 +552,7 @@ class PerThreadData : public PerThreadDataFriendFields friend class js::ActivationIterator; friend class js::jit::JitActivation; friend class js::AsmJSActivation; + friend class js::jit::CompileRuntime; #ifdef DEBUG friend void js::AssertCurrentThreadCanLock(RuntimeLock which); #endif From 45cbaa636c4698494d6f0826ecce61ae6b85a526 Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 15 Jan 2015 20:11:21 -0500 Subject: [PATCH 046/133] Bug 1057082 - 3/7 - Modify jits to use lastProfilingFrame and lastProfilingCallSite fields. r=jandem --- js/src/jit/Bailouts.cpp | 31 ++++++++++ js/src/jit/BaselineBailouts.cpp | 6 ++ js/src/jit/BaselineCompiler.cpp | 59 ++++++++++++++++++- js/src/jit/BaselineCompiler.h | 3 + js/src/jit/BaselineIC.cpp | 15 +++++ js/src/jit/BaselineJIT.cpp | 43 ++++++++++++-- js/src/jit/BaselineJIT.h | 29 +++++++-- js/src/jit/CodeGenerator.cpp | 8 ++- js/src/jit/IonCaches.cpp | 2 +- js/src/jit/JitCompartment.h | 6 +- js/src/jit/JitFrames.cpp | 43 ++++++++++++++ js/src/jit/JitFrames.h | 8 +++ js/src/jit/LIR-Common.h | 12 ++-- js/src/jit/Lowering.cpp | 2 +- js/src/jit/MIRGenerator.h | 10 +--- js/src/jit/arm/CodeGenerator-arm.cpp | 11 ++++ js/src/jit/arm/MacroAssembler-arm.cpp | 12 ++++ js/src/jit/arm/Trampoline-arm.cpp | 24 ++++++++ js/src/jit/mips/CodeGenerator-mips.cpp | 10 ++++ js/src/jit/mips/MacroAssembler-mips.cpp | 12 ++++ js/src/jit/mips/Trampoline-mips.cpp | 33 ++++++++++- js/src/jit/shared/BaselineCompiler-shared.cpp | 2 + js/src/jit/shared/BaselineCompiler-shared.h | 2 + js/src/jit/shared/CodeGenerator-shared.cpp | 2 +- js/src/jit/shared/CodeGenerator-shared.h | 4 +- .../jit/shared/CodeGenerator-x86-shared.cpp | 9 +++ js/src/jit/x64/MacroAssembler-x64.cpp | 11 ++++ js/src/jit/x64/Trampoline-x64.cpp | 28 ++++++++- js/src/jit/x86/MacroAssembler-x86.cpp | 12 ++++ js/src/jit/x86/Trampoline-x86.cpp | 28 ++++++++- js/src/vm/Runtime.h | 3 + js/src/vm/SPSProfiler.cpp | 43 ++++++++++++++ js/src/vm/SPSProfiler.h | 4 ++ 33 files changed, 486 insertions(+), 41 deletions(-) diff --git a/js/src/jit/Bailouts.cpp b/js/src/jit/Bailouts.cpp index 25fa715d19e7..23675a89d793 100644 --- a/js/src/jit/Bailouts.cpp +++ b/js/src/jit/Bailouts.cpp @@ -40,6 +40,7 @@ jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) BailoutFrameInfo bailoutData(jitActivations, sp); JitFrameIterator iter(jitActivations); MOZ_ASSERT(!iter.ionScript()->invalidated()); + CommonFrameLayout *currentFramePtr = iter.current(); TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime()); TraceLogTimestamp(logger, TraceLogger_Bailout); @@ -89,6 +90,26 @@ jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) if (iter.ionScript()->invalidated()) iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); + // NB: Commentary on how |lastProfilingFrame| is set from bailouts. + // + // Once we return to jitcode, any following frames might get clobbered, + // but the current frame will not (as it will be clobbered "in-place" + // with a baseline frame that will share the same frame prefix). + // However, there may be multiple baseline frames unpacked from this + // single Ion frame, which means we will need to once again reset + // |lastProfilingFrame| to point to the correct unpacked last frame + // in |FinishBailoutToBaseline|. + // + // In the case of error, the jitcode will jump immediately to an + // exception handler, which will unwind the frames and properly set + // the |lastProfilingFrame| to point to the frame being resumed into + // (see |AutoResetLastProfilerFrameOnReturnFromException|). + // + // In both cases, we want to temporarily set the |lastProfilingFrame| + // to the current frame being bailed out, and then fix it up later. + if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) + cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr); + return retval; } @@ -106,6 +127,7 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, sp); JitFrameIterator iter(jitActivations); + CommonFrameLayout *currentFramePtr = iter.current(); TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime()); TraceLogTimestamp(logger, TraceLogger_Invalidation); @@ -161,6 +183,10 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); + // Make the frame being bailed out the top profiled frame. + if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) + cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr); + return retval; } @@ -194,6 +220,7 @@ jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, frame.frame()); JitFrameIterator iter(jitActivations); + CommonFrameLayout *currentFramePtr = iter.current(); BaselineBailoutInfo *bailoutInfo = nullptr; uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, @@ -226,6 +253,10 @@ jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, MOZ_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR); } + // Make the frame being bailed out the top profiled frame. + if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) + cx->mainThread().jitActivation->setLastProfilingFrame(currentFramePtr); + return retval; } diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index a46e4f1f8baf..8609a3a56a0b 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -1721,6 +1721,12 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo) JitFrameIterator iter(cx); uint8_t *outerFp = nullptr; + // Iter currently points at the exit frame. Get the previous frame + // (which must be a baseline frame), and set it as the last profiling + // frame. + if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) + cx->mainThread().jitActivation->setLastProfilingFrame(iter.prevFp()); + uint32_t frameno = 0; while (frameno < numFrames) { MOZ_ASSERT(!iter.isIonJS()); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index db7e52b5281c..f88f1a0a742d 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -176,6 +176,8 @@ BaselineCompiler::compile() prologueOffset_.fixup(&masm); epilogueOffset_.fixup(&masm); spsPushToggleOffset_.fixup(&masm); + profilerEnterFrameToggleOffset_.fixup(&masm); + profilerExitFrameToggleOffset_.fixup(&masm); #ifdef JS_TRACE_LOGGING traceLoggerEnterToggleOffset_.fixup(&masm); traceLoggerExitToggleOffset_.fixup(&masm); @@ -189,6 +191,8 @@ BaselineCompiler::compile() BaselineScript::New(script, prologueOffset_.offset(), epilogueOffset_.offset(), spsPushToggleOffset_.offset(), + profilerEnterFrameToggleOffset_.offset(), + profilerExitFrameToggleOffset_.offset(), traceLoggerEnterToggleOffset_.offset(), traceLoggerExitToggleOffset_.offset(), postDebugPrologueOffset_.offset(), @@ -263,8 +267,9 @@ BaselineCompiler::compile() if (compileDebugInstrumentation_) baselineScript->setHasDebugInstrumentation(); - // Register a native => bytecode mapping entry for this script if needed. - if (cx->runtime()->jitRuntime()->isNativeToBytecodeMapEnabled(cx->runtime())) { + // If profiler instrumentation is enabled, register a native => bytecode mapping entry, + // and toggle profiling on + if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) { JitSpew(JitSpew_Profiling, "Added JitcodeGlobalEntry for baseline script %s:%d (%p)", script->filename(), script->lineno(), baselineScript.get()); JitcodeGlobalEntry::BaselineEntry entry; @@ -276,6 +281,9 @@ BaselineCompiler::compile() // Mark the jitcode as having a bytecode map. code->setHasBytecodeMap(); + + // Toggle profiler instrumentation on in the jitcode. + baselineScript->toggleProfilerInstrumentation(true); } script->setBaselineScript(cx, baselineScript.release()); @@ -324,6 +332,8 @@ BaselineCompiler::emitPrologue() masm.pushReturnAddress(); masm.checkStackAlignment(); #endif + emitProfilerEnterFrame(); + masm.push(BaselineFrameReg); masm.mov(BaselineStackReg, BaselineFrameReg); @@ -438,6 +448,8 @@ BaselineCompiler::emitEpilogue() masm.mov(BaselineFrameReg, BaselineStackReg); masm.pop(BaselineFrameReg); + emitProfilerExitFrame(); + masm.ret(); return true; } @@ -856,6 +868,36 @@ BaselineCompiler::emitSPSPop() masm.bind(&noPop); } +void +BaselineCompiler::emitProfilerEnterFrame() +{ + // Store stack position to lastProfilingFrame variable, guarded by a toggled jump. + // Starts off initially disabled. + Label noInstrument; + CodeOffsetLabel toggleOffset = masm.toggledJump(&noInstrument); + masm.profilerEnterFrame(BaselineStackReg, R0.scratchReg()); + masm.bind(&noInstrument); + + // Store the start offset in the appropriate location. + MOZ_ASSERT(profilerEnterFrameToggleOffset_.offset() == 0); + profilerEnterFrameToggleOffset_ = toggleOffset; +} + +void +BaselineCompiler::emitProfilerExitFrame() +{ + // Store previous frame to lastProfilingFrame variable, guarded by a toggled jump. + // Starts off initially disabled. + Label noInstrument; + CodeOffsetLabel toggleOffset = masm.toggledJump(&noInstrument); + masm.profilerExitFrame(); + masm.bind(&noInstrument); + + // Store the start offset in the appropriate location. + MOZ_ASSERT(profilerExitFrameToggleOffset_.offset() == 0); + profilerExitFrameToggleOffset_ = toggleOffset; +} + MethodStatus BaselineCompiler::emitBody() { @@ -3630,6 +3672,19 @@ BaselineCompiler::emit_JSOP_RESUME() masm.jump(&returnTarget); masm.bind(&genStart); + // If profiler instrumentation is on, update lastProfilingFrame on + // current JitActivation + { + Register scratchReg = scratch2; + Label skip; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skip); + masm.loadPtr(AbsoluteAddress(cx->mainThread().addressOfProfilingActivation()), scratchReg); + masm.storePtr(BaselineStackReg, + Address(scratchReg, JitActivation::offsetOfLastProfilingFrame())); + masm.bind(&skip); + } + // Construct BaselineFrame. masm.push(BaselineFrameReg); masm.mov(BaselineStackReg, BaselineFrameReg); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 067547696160..75bdfa66e0bf 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -259,6 +259,9 @@ class BaselineCompiler : public BaselineCompilerSpecific bool emitSPSPush(); void emitSPSPop(); + void emitProfilerEnterFrame(); + void emitProfilerExitFrame(); + bool initScopeChain(); void storeValue(const StackValue *source, const Address &dest, diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index d6647cc8a5cc..1fba957d3dab 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -1078,6 +1078,21 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler &masm) // the stack. masm.pop(scratchReg); +#ifdef DEBUG + // If profiler instrumentation is on, ensure that lastProfilingFrame is + // the frame currently being OSR-ed + { + Label checkOk; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &checkOk); + masm.loadPtr(AbsoluteAddress((void*)&cx->mainThread().jitActivation), scratchReg); + masm.loadPtr(Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()), scratchReg); + masm.branchPtr(Assembler::Equal, scratchReg, BaselineStackReg, &checkOk); + masm.assumeUnreachable("Baseline OSR lastProfilingFrame mismatch."); + masm.bind(&checkOk); + } +#endif + // Jump into Ion. masm.loadPtr(Address(osrDataReg, offsetof(IonOsrTempData, jitcode)), scratchReg); masm.loadPtr(Address(osrDataReg, offsetof(IonOsrTempData, baselineFrame)), OsrFrameReg); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index bf52d8926a64..8ed6fc947a0c 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -42,7 +42,10 @@ PCMappingSlotInfo::ToSlotLocation(const StackValue *stackVal) } BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, - uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset, + uint32_t spsPushToggleOffset, + uint32_t profilerEnterToggleOffset, + uint32_t profilerExitToggleOffset, + uint32_t traceLoggerEnterToggleOffset, uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset) : method_(nullptr), @@ -55,6 +58,8 @@ BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, spsOn_(false), #endif spsPushToggleOffset_(spsPushToggleOffset), + profilerEnterToggleOffset_(profilerEnterToggleOffset), + profilerExitToggleOffset_(profilerExitToggleOffset), #ifdef JS_TRACE_LOGGING # ifdef DEBUG traceLoggerScriptsEnabled_(false), @@ -342,8 +347,10 @@ jit::CanEnterBaselineMethod(JSContext *cx, RunState &state) BaselineScript * BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilogueOffset, - uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset, - uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset, + uint32_t spsPushToggleOffset, + uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, + uint32_t traceLoggerEnterToggleOffset, uint32_t traceLoggerExitToggleOffset, + uint32_t postDebugPrologueOffset, size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize, size_t bytecodeTypeMapEntries, size_t yieldEntries) { @@ -370,8 +377,10 @@ BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilog if (!script) return nullptr; new (script) BaselineScript(prologueOffset, epilogueOffset, - spsPushToggleOffset, traceLoggerEnterToggleOffset, - traceLoggerExitToggleOffset, postDebugPrologueOffset); + spsPushToggleOffset, + profilerEnterToggleOffset, profilerExitToggleOffset, + traceLoggerEnterToggleOffset, traceLoggerExitToggleOffset, + postDebugPrologueOffset); size_t offsetCursor = sizeof(BaselineScript); MOZ_ASSERT(offsetCursor == AlignBytes(sizeof(BaselineScript), DataAlignment)); @@ -950,6 +959,29 @@ BaselineScript::toggleTraceLoggerEngine(bool enable) } #endif +void +BaselineScript::toggleProfilerInstrumentation(bool enable) +{ + if (enable == isProfilerInstrumentationOn()) + return; + + JitSpew(JitSpew_BaselineIC, " toggling SPS %s for BaselineScript %p", + enable ? "on" : "off", this); + + // Toggle the jump + CodeLocationLabel enterToggleLocation(method_, CodeOffsetLabel(profilerEnterToggleOffset_)); + CodeLocationLabel exitToggleLocation(method_, CodeOffsetLabel(profilerExitToggleOffset_)); + if (enable) { + Assembler::ToggleToCmp(enterToggleLocation); + Assembler::ToggleToCmp(exitToggleLocation); + flags_ |= uint32_t(PROFILER_INSTRUMENTATION_ON); + } else { + Assembler::ToggleToJmp(enterToggleLocation); + Assembler::ToggleToJmp(exitToggleLocation); + flags_ &= ~uint32_t(PROFILER_INSTRUMENTATION_ON); + } +} + void BaselineScript::purgeOptimizedStubs(Zone *zone) { @@ -1053,6 +1085,7 @@ jit::ToggleBaselineSPS(JSRuntime *runtime, bool enable) if (!script->hasBaselineScript()) continue; script->baselineScript()->toggleSPS(enable); + script->baselineScript()->toggleProfilerInstrumentation(enable); } } } diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 714efa59d46f..8ffc4be91eaa 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -144,6 +144,8 @@ struct BaselineScript mozilla::DebugOnly spsOn_; #endif uint32_t spsPushToggleOffset_; + uint32_t profilerEnterToggleOffset_; + uint32_t profilerExitToggleOffset_; // The offsets and event used for Tracelogger toggling. #ifdef JS_TRACE_LOGGING @@ -185,7 +187,10 @@ struct BaselineScript // Flag set if this script has ever been Ion compiled, either directly // or inlined into another script. This is cleared when the script's // type information or caches are cleared. - ION_COMPILED_OR_INLINED = 1 << 4 + ION_COMPILED_OR_INLINED = 1 << 4, + + // Flag is set if this script has profiling instrumentation turned on. + PROFILER_INSTRUMENTATION_ON = 1 << 5 }; private: @@ -214,14 +219,22 @@ struct BaselineScript public: // Do not call directly, use BaselineScript::New. This is public for cx->new_. BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, - uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset, - uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset); + uint32_t spsPushToggleOffset, + uint32_t profilerEnterToggleOffset, + uint32_t profilerExitToggleOffset, + uint32_t traceLoggerEnterToggleOffset, + uint32_t traceLoggerExitToggleOffset, + uint32_t postDebugPrologueOffset); static BaselineScript *New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilogueOffset, uint32_t postDebugPrologueOffset, - uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset, - uint32_t traceLoggerExitToggleOffset, size_t icEntries, - size_t pcMappingIndexEntries, size_t pcMappingSize, + uint32_t spsPushToggleOffset, + uint32_t profilerEnterToggleOffset, + uint32_t profilerExitToggleOffset, + uint32_t traceLoggerEnterToggleOffset, + uint32_t traceLoggerExitToggleOffset, + size_t icEntries, size_t pcMappingIndexEntries, + size_t pcMappingSize, size_t bytecodeTypeMapEntries, size_t yieldEntries); static void Trace(JSTracer *trc, BaselineScript *script); @@ -386,6 +399,10 @@ struct BaselineScript void toggleDebugTraps(JSScript *script, jsbytecode *pc); void toggleSPS(bool enable); + void toggleProfilerInstrumentation(bool enable); + bool isProfilerInstrumentationOn() const { + return flags_ & PROFILER_INSTRUMENTATION_ON; + } #ifdef JS_TRACE_LOGGING void initTraceLogger(JSRuntime *runtime, JSScript *script); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 1afb67c5f471..0a6b38eaf7dc 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1981,6 +1981,8 @@ CodeGenerator::visitReturn(LReturn *lir) void CodeGenerator::visitOsrEntry(LOsrEntry *lir) { + Register temp = ToRegister(lir->temp()); + // Remember the OSR entry offset into the code buffer. masm.flushBuffer(); setOsrEntryOffset(masm.size()); @@ -1990,6 +1992,10 @@ CodeGenerator::visitOsrEntry(LOsrEntry *lir) emitTracelogStartEvent(TraceLogger_IonMonkey); #endif + // If profiling, save the current frame pointer to a per-thread global field. + if (isProfilerInstrumentationEnabled()) + masm.profilerEnterFrame(StackPointer, temp); + // Allocate the full frame for this function // Note we have a new entry here. So we reset MacroAssembler::framePushed() // to 0, before reserving the stack. @@ -7154,7 +7160,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) return false; // Encode native to bytecode map if profiling is enabled. - if (isNativeToBytecodeMapEnabled()) { + if (isProfilerInstrumentationEnabled()) { // Generate native-to-bytecode main table. if (!generateCompactNativeToBytecodeMap(cx, code)) return false; diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 0a284d603752..13799c92aaef 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -429,7 +429,7 @@ IonCache::linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &a attachStub(masm, attacher, code); // Add entry to native => bytecode mapping for this stub if needed. - if (cx->runtime()->jitRuntime()->isNativeToBytecodeMapEnabled(cx->runtime())) { + if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) { JitcodeGlobalEntry::IonCacheEntry entry; entry.init(code->raw(), code->raw() + code->instructionsSize(), rejoinAddress()); diff --git a/js/src/jit/JitCompartment.h b/js/src/jit/JitCompartment.h index d28234723f25..877b8867e601 100644 --- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -399,12 +399,8 @@ class JitRuntime return jitcodeGlobalTable_; } - bool isNativeToBytecodeMapEnabled(JSRuntime *rt) { -#ifdef DEBUG - return true; -#else // DEBUG + bool isProfilerInstrumentationEnabled(JSRuntime *rt) { return rt->spsProfiler.enabled(); -#endif // DEBUG } }; diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 785246a66643..cc5245e489ee 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -29,6 +29,7 @@ #include "vm/ArgumentsObject.h" #include "vm/Debugger.h" #include "vm/Interpreter.h" +#include "vm/SPSProfiler.h" #include "vm/TraceLogging.h" #include "jsinferinlines.h" @@ -682,12 +683,54 @@ struct AutoClearBaselineOverridePc ~AutoClearBaselineOverridePc() { frame->clearOverridePc(); } }; +struct AutoResetLastProfilerFrameOnReturnFromException +{ + JSContext *cx; + ResumeFromException *rfe; + + AutoResetLastProfilerFrameOnReturnFromException(JSContext *cx, ResumeFromException *rfe) + : cx(cx), rfe(rfe) {} + + ~AutoResetLastProfilerFrameOnReturnFromException() { + if (!cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) + return; + + MOZ_ASSERT(cx->mainThread().jitActivation == cx->mainThread().profilingActivation()); + + void *lastProfilingFrame = getLastProfilingFrame(); + cx->mainThread().jitActivation->setLastProfilingFrame(lastProfilingFrame); + } + + void *getLastProfilingFrame() { + switch (rfe->kind) { + case ResumeFromException::RESUME_ENTRY_FRAME: + return nullptr; + + // The following all return into baseline frames. + case ResumeFromException::RESUME_CATCH: + case ResumeFromException::RESUME_FINALLY: + case ResumeFromException::RESUME_FORCED_RETURN: + return rfe->framePointer + BaselineFrame::FramePointerOffset; + + // When resuming into a bailed-out ion frame, use the bailout info to + // find the frame we are resuming into. + case ResumeFromException::RESUME_BAILOUT: + return rfe->bailoutInfo->incomingStack; + } + + MOZ_CRASH("Invalid ResumeFromException type!"); + return nullptr; + } +}; + void HandleException(ResumeFromException *rfe) { JSContext *cx = GetJSContextFromJitCode(); TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime()); + AutoResetLastProfilerFrameOnReturnFromException profFrameReset(cx, rfe); + rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME; JitSpew(JitSpew_IonInvalidate, "handling exception"); diff --git a/js/src/jit/JitFrames.h b/js/src/jit/JitFrames.h index 3ca7ee17cdb9..9c13dbb0fd2f 100644 --- a/js/src/jit/JitFrames.h +++ b/js/src/jit/JitFrames.h @@ -331,6 +331,9 @@ class CommonFrameLayout static size_t offsetOfDescriptor() { return offsetof(CommonFrameLayout, descriptor_); } + uintptr_t descriptor() const { + return descriptor_; + } static size_t offsetOfReturnAddress() { return offsetof(CommonFrameLayout, returnAddress_); } @@ -823,6 +826,11 @@ class BaselineStubFrameLayout : public CommonFrameLayout return -int(2 * sizeof(void *)); } + void *reverseSavedFramePtr() { + uint8_t *addr = ((uint8_t *) this) + reverseOffsetOfSavedFramePtr(); + return *(void **)addr; + } + inline ICStub *maybeStubPtr() { uint8_t *fp = reinterpret_cast(this); return *reinterpret_cast(fp + reverseOffsetOfStubPtr()); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 27eaad1a6a6a..2d88ab11296b 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -3666,7 +3666,7 @@ class LStart : public LInstructionHelper<0, 0, 0> // Passed the BaselineFrame address in the OsrFrameReg by SideCannon(). // Forwards this object to the LOsrValues for Value materialization. -class LOsrEntry : public LInstructionHelper<1, 0, 0> +class LOsrEntry : public LInstructionHelper<1, 0, 1> { protected: Label label_; @@ -3675,9 +3675,11 @@ class LOsrEntry : public LInstructionHelper<1, 0, 0> public: LIR_HEADER(OsrEntry) - LOsrEntry() + LOsrEntry(const LDefinition &temp) : frameDepth_(0) - { } + { + setTemp(0, temp); + } void setFrameDepth(uint32_t depth) { frameDepth_ = depth; @@ -3688,7 +3690,9 @@ class LOsrEntry : public LInstructionHelper<1, 0, 0> Label *label() { return &label_; } - + const LDefinition *temp() { + return getTemp(0); + } }; // Materialize a Value stored in an interpreter frame for OSR. diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index d40d214e0890..7d9d705f193d 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1633,7 +1633,7 @@ LIRGenerator::visitLimitedTruncate(MLimitedTruncate *nop) void LIRGenerator::visitOsrEntry(MOsrEntry *entry) { - LOsrEntry *lir = new(alloc()) LOsrEntry; + LOsrEntry *lir = new(alloc()) LOsrEntry(temp()); defineFixed(lir, entry, LAllocation(AnyRegister(OsrFrameReg))); } diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index 54c7df8794c8..a5e5e7242884 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -83,14 +83,8 @@ class MIRGenerator return instrumentedProfiling_; } - bool isNativeToBytecodeMapEnabled() { - if (compilingAsmJS()) - return false; -#ifdef DEBUG - return true; -#else - return instrumentedProfiling(); -#endif + bool isProfilerInstrumentationEnabled() { + return !compilingAsmJS() && instrumentedProfiling(); } // Whether the main thread is trying to cancel this build. diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index 496998fddb82..b1aa80468907 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -47,6 +47,11 @@ CodeGeneratorARM::generatePrologue() #ifdef JS_USE_LINK_REGISTER masm.pushReturnAddress(); #endif + + // If profiling, save the current frame pointer to a per-thread global field. + if (isProfilerInstrumentationEnabled()) + masm.profilerEnterFrame(StackPointer, CallTempReg0); + // Note that this automatically sets MacroAssembler::framePushed(). masm.reserveStack(frameSize()); masm.checkStackAlignment(); @@ -66,6 +71,12 @@ CodeGeneratorARM::generateEpilogue() masm.freeStack(frameSize()); MOZ_ASSERT(masm.framePushed() == 0); + + // If profiling, reset the per-thread global lastJitFrame to point to + // the previous frame. + if (isProfilerInstrumentationEnabled()) + masm.profilerExitFrame(); + masm.pop(pc); masm.flushBuffer(); return true; diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index ec3ed33cf282..0a6c42ad5682 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -4294,6 +4294,18 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void *handler) loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); ma_mov(r11, sp); pop(r11); + + // If profiling is enabled, then update the lastProfilingFrame to refer to caller + // frame before returning. + { + Label skipProfilingInstrumentation; + // Test if profiler enabled. + AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->spsProfiler().addressOfEnabled()); + branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation); + profilerExitFrame(); + bind(&skipProfilingInstrumentation); + } + ret(); // If we are bailing out to baseline to handle an exception, jump to the diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index d43596e72221..ccc9740b63f5 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -283,6 +283,19 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); masm.branchIfFalseBool(ReturnReg, &error); + // If OSR-ing, then emit instrumentation for setting lastProfilerFrame + // if profiler instrumentation is enabled. + { + Label skipProfilingInstrumentation; + Register realFramePtr = numStackValues; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), + &skipProfilingInstrumentation); + masm.ma_add(framePtr, Imm32(sizeof(void*)), realFramePtr); + masm.profilerEnterFrame(realFramePtr, scratch); + masm.bind(&skipProfilingInstrumentation); + } + masm.jump(jitcode); // OOM: Load error value, discard return address and previous frame @@ -949,6 +962,17 @@ JitRuntime::generateDebugTrapHandler(JSContext *cx) JSReturnOperand); masm.mov(r11, sp); masm.pop(r11); + + // Before returning, if profiling is turned on, make sure that lastProfilingFrame + // is set to the correct caller frame. + { + Label skipProfilingInstrumentation; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation); + masm.profilerExitFrame(); + masm.bind(&skipProfilingInstrumentation); + } + masm.ret(); Linker linker(masm); diff --git a/js/src/jit/mips/CodeGenerator-mips.cpp b/js/src/jit/mips/CodeGenerator-mips.cpp index 7ac3c91a3dd5..fd3e32b873ad 100644 --- a/js/src/jit/mips/CodeGenerator-mips.cpp +++ b/js/src/jit/mips/CodeGenerator-mips.cpp @@ -45,6 +45,10 @@ CodeGeneratorMIPS::generatePrologue() MOZ_ASSERT(masm.framePushed() == 0); MOZ_ASSERT(!gen->compilingAsmJS()); + // If profiling, save the current frame pointer to a per-thread global field. + if (isProfilerInstrumentationEnabled()) + masm.profilerEnterFrame(StackPointer, CallTempReg0); + // Note that this automatically sets MacroAssembler::framePushed(). masm.reserveStack(frameSize()); masm.checkStackAlignment(); @@ -64,6 +68,12 @@ CodeGeneratorMIPS::generateEpilogue() masm.freeStack(frameSize()); MOZ_ASSERT(masm.framePushed() == 0); + + // If profiling, reset the per-thread global lastJitFrame to point to + // the previous frame. + if (isProfilerInstrumentationEnabled()) + masm.profilerExitFrame(); + masm.ret(); return true; } diff --git a/js/src/jit/mips/MacroAssembler-mips.cpp b/js/src/jit/mips/MacroAssembler-mips.cpp index e7db4ac47ac0..81d880619dd3 100644 --- a/js/src/jit/mips/MacroAssembler-mips.cpp +++ b/js/src/jit/mips/MacroAssembler-mips.cpp @@ -3604,6 +3604,18 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void *handler) JSReturnOperand); ma_move(StackPointer, BaselineFrameReg); pop(BaselineFrameReg); + + // If profiling is enabled, then update the lastProfilingFrame to refer to caller + // frame before returning. + { + Label skipProfilingInstrumentation; + // Test if profiler enabled. + AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->spsProfiler().addressOfEnabled()); + branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation); + profilerExitFrame(); + bind(&skipProfilingInstrumentation); + } + ret(); // If we are bailing out to baseline to handle an exception, jump to diff --git a/js/src/jit/mips/Trampoline-mips.cpp b/js/src/jit/mips/Trampoline-mips.cpp index 6cc82ffb6996..552f19877ad3 100644 --- a/js/src/jit/mips/Trampoline-mips.cpp +++ b/js/src/jit/mips/Trampoline-mips.cpp @@ -209,10 +209,13 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) Register numStackValues = regs.takeAny(); masm.load32(slotNumStackValues, numStackValues); - // Push return address, previous frame pointer. - masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); + // Push return address. + masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer); masm.ma_li(scratch, returnLabel.dest()); - masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); + masm.storePtr(scratch, Address(StackPointer, 0)); + + // Push previous frame pointer. + masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer); masm.storePtr(BaselineFrameReg, Address(StackPointer, 0)); // Reserve frame. @@ -261,6 +264,19 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); masm.branchIfFalseBool(ReturnReg, &error); + // If OSR-ing, then emit instrumentation for setting lastProfilerFrame + // if profiler instrumentation is enabled. + { + Label skipProfilingInstrumentation; + Register realFramePtr = numStackValues; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), + &skipProfilingInstrumentation); + masm.ma_add(realFramePtr, StackPointer, Imm32(sizeof(void*))); + masm.profilerEnterFrame(realFramePtr, scratch); + masm.bind(&skipProfilingInstrumentation); + } + masm.jump(jitcode); // OOM: load error value, discard return address and previous frame @@ -952,6 +968,17 @@ JitRuntime::generateDebugTrapHandler(JSContext *cx) JSReturnOperand); masm.movePtr(s5, StackPointer); masm.pop(s5); + + // Before returning, if profiling is turned on, make sure that lastProfilingFrame + // is set to the correct caller frame. + { + Label skipProfilingInstrumentation; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation); + masm.profilerExitFrame(); + masm.bind(&skipProfilingInstrumentation); + } + masm.ret(); Linker linker(masm); diff --git a/js/src/jit/shared/BaselineCompiler-shared.cpp b/js/src/jit/shared/BaselineCompiler-shared.cpp index 0ea502e23861..5d75980e9622 100644 --- a/js/src/jit/shared/BaselineCompiler-shared.cpp +++ b/js/src/jit/shared/BaselineCompiler-shared.cpp @@ -31,6 +31,8 @@ BaselineCompilerShared::BaselineCompilerShared(JSContext *cx, TempAllocator &all pushedBeforeCall_(0), inCall_(false), spsPushToggleOffset_(), + profilerEnterFrameToggleOffset_(), + profilerExitFrameToggleOffset_(), traceLoggerEnterToggleOffset_(), traceLoggerExitToggleOffset_(), traceLoggerScriptTextIdOffset_() diff --git a/js/src/jit/shared/BaselineCompiler-shared.h b/js/src/jit/shared/BaselineCompiler-shared.h index fa7740bc2245..425597865d71 100644 --- a/js/src/jit/shared/BaselineCompiler-shared.h +++ b/js/src/jit/shared/BaselineCompiler-shared.h @@ -68,6 +68,8 @@ class BaselineCompilerShared mozilla::DebugOnly inCall_; CodeOffsetLabel spsPushToggleOffset_; + CodeOffsetLabel profilerEnterFrameToggleOffset_; + CodeOffsetLabel profilerExitFrameToggleOffset_; CodeOffsetLabel traceLoggerEnterToggleOffset_; CodeOffsetLabel traceLoggerExitToggleOffset_; CodeOffsetLabel traceLoggerScriptTextIdOffset_; diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 639dbc4567ee..4d9f50f805ec 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -158,7 +158,7 @@ bool CodeGeneratorShared::addNativeToBytecodeEntry(const BytecodeSite *site) { // Skip the table entirely if profiling is not enabled. - if (!isNativeToBytecodeMapEnabled()) + if (!isProfilerInstrumentationEnabled()) return true; MOZ_ASSERT(site); diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index 6ee065850515..9c41212259ca 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -118,8 +118,8 @@ class CodeGeneratorShared : public LElementVisitor // scripts) and when instrumentation needs to be emitted or skipped. IonInstrumentation sps_; - bool isNativeToBytecodeMapEnabled() { - return gen->isNativeToBytecodeMapEnabled(); + bool isProfilerInstrumentationEnabled() { + return gen->isProfilerInstrumentationEnabled(); } protected: diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index 2224f50909c2..461df0f66c2d 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -44,6 +44,10 @@ CodeGeneratorX86Shared::generatePrologue() MOZ_ASSERT(masm.framePushed() == 0); MOZ_ASSERT(!gen->compilingAsmJS()); + // If profiling, save the current frame pointer to a per-thread global field. + if (isProfilerInstrumentationEnabled()) + masm.profilerEnterFrame(StackPointer, CallTempReg0); + // Note that this automatically sets MacroAssembler::framePushed(). masm.reserveStack(frameSize()); @@ -65,6 +69,11 @@ CodeGeneratorX86Shared::generateEpilogue() masm.freeStack(frameSize()); MOZ_ASSERT(masm.framePushed() == 0); + // If profiling, reset the per-thread global lastJitFrame to point to + // the previous frame. + if (isProfilerInstrumentationEnabled()) + masm.profilerExitFrame(); + masm.ret(); return true; } diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 428233dbd0f3..275d8c3610f5 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -441,6 +441,17 @@ MacroAssemblerX64::handleFailureWithHandlerTail(void *handler) loadValue(Address(rbp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); movq(rbp, rsp); pop(rbp); + + // If profiling is enabled, then update the lastProfilingFrame to refer to caller + // frame before returning. + { + Label skipProfilingInstrumentation; + AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->spsProfiler().addressOfEnabled()); + branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation); + profilerExitFrame(); + bind(&skipProfilingInstrumentation); + } + ret(); // If we are bailing out to baseline to handle an exception, jump to diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index a8cf6e64f26e..41bbff07c70c 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -165,9 +165,11 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) Register numStackValues = regs.takeAny(); masm.movq(numStackValuesAddr, numStackValues); - // Push return address, previous frame pointer. + // Push return address masm.mov(returnLabel.dest(), scratch); masm.push(scratch); + + // Push previous frame pointer. masm.push(rbp); // Reserve frame. @@ -231,6 +233,19 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); masm.branchIfFalseBool(ReturnReg, &error); + // If OSR-ing, then emit instrumentation for setting lastProfilerFrame + // if profiler instrumentation is enabled. + { + Label skipProfilingInstrumentation; + Register realFramePtr = numStackValues; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), + &skipProfilingInstrumentation); + masm.lea(Operand(framePtr, sizeof(void*)), realFramePtr); + masm.profilerEnterFrame(realFramePtr, scratch); + masm.bind(&skipProfilingInstrumentation); + } + masm.jump(reg_code); // OOM: load error value, discard return address and previous frame @@ -775,6 +790,17 @@ JitRuntime::generateDebugTrapHandler(JSContext *cx) JSReturnOperand); masm.mov(rbp, rsp); masm.pop(rbp); + + // Before returning, if profiling is turned on, make sure that lastProfilingFrame + // is set to the correct caller frame. + { + Label skipProfilingInstrumentation; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation); + masm.profilerExitFrame(); + masm.bind(&skipProfilingInstrumentation); + } + masm.ret(); Linker linker(masm); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index 23b4f418fc29..d83a664957a4 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -421,6 +421,18 @@ MacroAssemblerX86::handleFailureWithHandlerTail(void *handler) loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); movl(ebp, esp); pop(ebp); + + // If profiling is enabled, then update the lastProfilingFrame to refer to caller + // frame before returning. + { + Label skipProfilingInstrumentation; + // Test if profiler enabled. + AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->spsProfiler().addressOfEnabled()); + branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation); + profilerExitFrame(); + bind(&skipProfilingInstrumentation); + } + ret(); // If we are bailing out to baseline to handle an exception, jump to diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index af180bca799a..baddb5a1f39c 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -159,9 +159,11 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) Register jitcode = regs.takeAny(); masm.loadPtr(Address(ebp, ARG_JITCODE), jitcode); - // Push return address, previous frame pointer. + // Push return address. masm.mov(returnLabel.dest(), scratch); masm.push(scratch); + + // Push previous frame pointer. masm.push(ebp); // Reserve frame. @@ -222,6 +224,19 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); masm.branchIfFalseBool(ReturnReg, &error); + // If OSR-ing, then emit instrumentation for setting lastProfilerFrame + // if profiler instrumentation is enabled. + { + Label skipProfilingInstrumentation; + Register realFramePtr = numStackValues; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), + &skipProfilingInstrumentation); + masm.lea(Operand(framePtr, sizeof(void*)), realFramePtr); + masm.profilerEnterFrame(realFramePtr, scratch); + masm.bind(&skipProfilingInstrumentation); + } + masm.jump(jitcode); // OOM: load error value, discard return address and previous frame @@ -813,6 +828,17 @@ JitRuntime::generateDebugTrapHandler(JSContext *cx) JSReturnOperand); masm.mov(ebp, esp); masm.pop(ebp); + + // Before returning, if profiling is turned on, make sure that lastProfilingFrame + // is set to the correct caller frame. + { + Label skipProfilingInstrumentation; + AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled()); + masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation); + masm.profilerExitFrame(); + masm.bind(&skipProfilingInstrumentation); + } + masm.ret(); Linker linker(masm); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index b6cc12317e4c..951996f7eed9 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -594,6 +594,9 @@ class PerThreadData : public PerThreadDataFriendFields js::Activation *profilingActivation() const { return profilingActivation_; } + void *addressOfProfilingActivation() { + return (void*) &profilingActivation_; + } js::AsmJSActivation *asmJSActivationStack() const { return asmJSActivationStack_; diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index d41189d51385..d7b2f2fb68e8 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -12,7 +12,9 @@ #include "jsprf.h" #include "jsscript.h" +#include "jit/BaselineFrame.h" #include "jit/BaselineJIT.h" +#include "jit/JitFrames.h" #include "vm/StringBuffer.h" using namespace js; @@ -92,6 +94,14 @@ SPSProfiler::enable(bool enabled) * their profiler state toggled so they behave properly. */ jit::ToggleBaselineSPS(rt, enabled); + + /* Update lastProfilingFrame to point to the top-most JS jit-frame currently on + * stack. + */ + if (rt->mainThread.jitActivation) { + void *lastProfilingFrame = GetTopProfilingJitFrame(rt->mainThread.jitTop); + rt->mainThread.jitActivation->setLastProfilingFrame(lastProfilingFrame); + } } /* Lookup the string for the function/script, creating one if necessary */ @@ -402,3 +412,36 @@ AutoSuppressProfilerSampling::~AutoSuppressProfilerSampling() if (previouslyEnabled_) rt_->enableProfilerSampling(); } + +void * +js::GetTopProfilingJitFrame(uint8_t *exitFramePtr) +{ + // For null exitFrame, there is no previous exit frame, just return. + if (!exitFramePtr) + return nullptr; + + jit::ExitFrameLayout *exitFrame = (jit::ExitFrameLayout *) exitFramePtr; + size_t prevSize = exitFrame->prevFrameLocalSize(); + jit::FrameType prevType = exitFrame->prevType(); + + uint8_t *prev = exitFramePtr + (jit::ExitFrameLayout::Size() + prevSize); + + // previous frame type must be one of IonJS, BaselineJS, or BaselineStub, + // or unwound variants thereof. + switch (prevType) { + case jit::JitFrame_IonJS: + case jit::JitFrame_Unwound_IonJS: + case jit::JitFrame_BaselineJS: + return prev; + + case jit::JitFrame_BaselineStub: + case jit::JitFrame_Unwound_BaselineStub: { + void *framePtr = ((jit::BaselineStubFrameLayout *) prev)->reverseSavedFramePtr(); + return ((uint8_t *) framePtr) + jit::BaselineFrame::FramePointerOffset; + } + + default: + MOZ_CRASH("unknown callee token type"); + return nullptr; + } +} diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h index 5c77368d5a6a..9955d1da5012 100644 --- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -513,6 +513,10 @@ class SPSInstrumentation } }; + +/* Get a pointer to the top-most profiling frame, given the exit frame pointer. */ +void *GetTopProfilingJitFrame(uint8_t *exitFramePtr); + } /* namespace js */ #endif /* vm_SPSProfiler_h */ From 0aae8b17a15150800232716e8e77949a6116278d Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 15 Jan 2015 20:11:21 -0500 Subject: [PATCH 047/133] Bug 1057082 - 4/7 - Add assembler helpers for later patches. r=jandem --- js/src/jit/MacroAssembler.cpp | 49 ++++++++++++++++++++++++++++ js/src/jit/MacroAssembler.h | 19 +++-------- js/src/jit/shared/Assembler-shared.h | 5 +++ 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index f17a5259e969..f3328838885f 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1925,6 +1925,30 @@ MacroAssembler::finish() MacroAssemblerSpecific::finish(); } +void +MacroAssembler::link(JitCode *code) +{ + MOZ_ASSERT(!oom()); + // If this code can transition to C++ code and witness a GC, then we need to store + // the JitCode onto the stack in order to GC it correctly. exitCodePatch should + // be unset if the code never needed to push its JitCode*. + if (hasEnteredExitFrame()) { + exitCodePatch_.fixup(this); + PatchDataWithValueCheck(CodeLocationLabel(code, exitCodePatch_), + ImmPtr(code), + ImmPtr((void*)-1)); + } + + // Fix up the code pointers to be written for locations where profilerCallSite + // emitted moves of RIP to a register. + for (size_t i = 0; i < profilerCallSites_.length(); i++) { + CodeOffsetLabel offset = profilerCallSites_[i]; + offset.fixup(this); + CodeLocationLabel location(code, offset); + PatchDataWithValueCheck(location, ImmPtr(location.raw()), ImmPtr((void*)-1)); + } +} + void MacroAssembler::branchIfNotInterpretedConstructor(Register fun, Register scratch, Label *label) { @@ -2050,3 +2074,28 @@ MacroAssembler::spsUnmarkJit(SPSProfiler *p, Register temp) bind(&spsNotEnabled); } + +void +MacroAssembler::profilerPreCallImpl() +{ + Register reg = CallTempReg0; + Register reg2 = CallTempReg1; + push(reg); + push(reg2); + profilerPreCallImpl(reg, reg2); + pop(reg2); + pop(reg); +} + +void +MacroAssembler::profilerPreCallImpl(Register reg, Register reg2) +{ + JitContext *icx = GetJitContext(); + AbsoluteAddress profilingActivation(icx->runtime->addressOfProfilingActivation()); + + CodeOffsetLabel label = movWithPatch(ImmWord(uintptr_t(-1)), reg); + loadPtr(profilingActivation, reg2); + storePtr(reg, Address(reg2, JitActivation::offsetOfLastProfilingCallSite())); + + appendProfilerCallSite(label); +} diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 1459c3f45496..ead3ffabdf80 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -864,20 +864,6 @@ class MacroAssembler : public MacroAssemblerSpecific return exitCodePatch_.offset() != 0; } - void link(JitCode *code) { - MOZ_ASSERT(!oom()); - // If this code can transition to C++ code and witness a GC, then we need to store - // the JitCode onto the stack in order to GC it correctly. exitCodePatch should - // be unset if the code never needed to push its JitCode*. - if (hasEnteredExitFrame()) { - exitCodePatch_.fixup(this); - PatchDataWithValueCheck(CodeLocationLabel(code, exitCodePatch_), - ImmPtr(code), - ImmPtr((void*)-1)); - } - - } - // Generates code used to complete a bailout. void generateBailoutTail(Register scratch, Register bailoutInfo); @@ -1151,6 +1137,7 @@ class MacroAssembler : public MacroAssemblerSpecific } void finish(); + void link(JitCode *code); void assumeUnreachable(const char *output); void printf(const char *output); @@ -1422,6 +1409,10 @@ class MacroAssembler : public MacroAssemblerSpecific bind(&ok); #endif } + + void profilerPreCallImpl(); + void profilerPreCallImpl(Register reg, Register reg2); + void profilerPostReturnImpl() {} }; static inline Assembler::DoubleCondition diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 01c9e1144ebf..2b4c064e603f 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -897,6 +897,7 @@ class AssemblerShared Vector asmJSAbsoluteLinks_; protected: + Vector profilerCallSites_; bool enoughMemory_; bool embedsNurseryPointers_; @@ -918,6 +919,10 @@ class AssemblerShared return !enoughMemory_; } + void appendProfilerCallSite(CodeOffsetLabel label) { + enoughMemory_ &= profilerCallSites_.append(label); + } + bool embedsNurseryPointers() const { return embedsNurseryPointers_; } From 6a0633fdc1c1940ce4d26ff905023c1b9b53ec6e Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 15 Jan 2015 20:11:22 -0500 Subject: [PATCH 048/133] Bug 1057082 - 5/7 - Remove SPS instrumentation and replace with exitaddr instrumentation. r=jandem --- js/src/jit/Bailouts.cpp | 33 +-- js/src/jit/Bailouts.h | 2 +- js/src/jit/BaselineBailouts.cpp | 77 +----- js/src/jit/BaselineCompiler.cpp | 41 ---- js/src/jit/BaselineCompiler.h | 2 - js/src/jit/BaselineFrame.cpp | 15 +- js/src/jit/BaselineFrame.h | 15 -- js/src/jit/BaselineIC.cpp | 270 +-------------------- js/src/jit/BaselineIC.h | 93 ------- js/src/jit/BaselineJIT.cpp | 31 +-- js/src/jit/BaselineJIT.h | 14 +- js/src/jit/CodeGenerator.cpp | 54 +---- js/src/jit/CodeGenerator.h | 1 - js/src/jit/Ion.cpp | 2 +- js/src/jit/IonBuilder.cpp | 5 - js/src/jit/IonCode.h | 16 +- js/src/jit/IonInstrumentation.h | 13 +- js/src/jit/JitFrames.cpp | 37 +-- js/src/jit/LIR-Common.h | 22 -- js/src/jit/LOpcodes.h | 1 - js/src/jit/Lowering.cpp | 12 - js/src/jit/Lowering.h | 1 - js/src/jit/MIR.h | 42 ---- js/src/jit/MOpcodes.h | 1 - js/src/jit/MacroAssembler.cpp | 76 +----- js/src/jit/MacroAssembler.h | 218 ++--------------- js/src/jit/VMFunctions.cpp | 22 -- js/src/jit/VMFunctions.h | 3 - js/src/jit/arm/Trampoline-arm.cpp | 12 +- js/src/jit/shared/CodeGenerator-shared.cpp | 11 +- js/src/jit/shared/CodeGenerator-shared.h | 5 - js/src/jit/x64/Trampoline-x64.cpp | 6 - js/src/jit/x86/Trampoline-x86.cpp | 20 +- js/src/vm/SPSProfiler.cpp | 3 +- js/src/vm/SPSProfiler.h | 213 +--------------- js/src/vm/Stack.cpp | 3 +- 36 files changed, 94 insertions(+), 1298 deletions(-) diff --git a/js/src/jit/Bailouts.cpp b/js/src/jit/Bailouts.cpp index 23675a89d793..2daf8612e422 100644 --- a/js/src/jit/Bailouts.cpp +++ b/js/src/jit/Bailouts.cpp @@ -50,31 +50,17 @@ jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; - bool poppedLastSPSFrame = false; uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo, - /* excInfo = */ nullptr, &poppedLastSPSFrame); + /* excInfo = */ nullptr); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); if (retval != BAILOUT_RETURN_OK) { - // If the bailout failed, then bailout trampoline will pop the - // current frame and jump straight to exception handling code when - // this function returns. Any SPS entry pushed for this frame will - // be silently forgotten. - // - // We call ExitScript here to ensure that if the ionScript had SPS - // instrumentation, then the SPS entry for it is popped. - // - // However, if the bailout was during argument check, then a - // pseudostack frame would not have been pushed in the first - // place, so don't pop anything in that case. - bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() && - (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) && - !poppedLastSPSFrame; JSScript *script = iter.script(); - probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); + probes::ExitScript(cx, script, script->functionNonDelazifying(), + /* popSPSFrame = */ false); EnsureExitFrame(iter.jsFrame()); } @@ -140,9 +126,8 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; - bool poppedLastSPSFrame = false; uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo, - /* excInfo = */ nullptr, &poppedLastSPSFrame); + /* excInfo = */ nullptr); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); @@ -160,11 +145,9 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, // However, if the bailout was during argument check, then a // pseudostack frame would not have been pushed in the first // place, so don't pop anything in that case. - bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() && - (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) && - !poppedLastSPSFrame; JSScript *script = iter.script(); - probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); + probes::ExitScript(cx, script, script->functionNonDelazifying(), + /* popSPSFrame = */ false); JitFrameLayout *frame = iter.jsFrame(); JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s): converting to exit frame", @@ -207,7 +190,7 @@ uint32_t jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe, const ExceptionBailoutInfo &excInfo, - bool *overrecursed, bool *poppedLastSPSFrameOut) + bool *overrecursed) { // We can be propagating debug mode exceptions without there being an // actual exception pending. For instance, when we return false from an @@ -224,7 +207,7 @@ jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, BaselineBailoutInfo *bailoutInfo = nullptr; uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, - &bailoutInfo, &excInfo, poppedLastSPSFrameOut); + &bailoutInfo, &excInfo); if (retval == BAILOUT_RETURN_OK) { MOZ_ASSERT(bailoutInfo); diff --git a/js/src/jit/Bailouts.h b/js/src/jit/Bailouts.h index c07f092b44fe..aaf6cc5e8e62 100644 --- a/js/src/jit/Bailouts.h +++ b/js/src/jit/Bailouts.h @@ -209,7 +209,7 @@ class ExceptionBailoutInfo uint32_t ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe, const ExceptionBailoutInfo &excInfo, - bool *overrecursed, bool *poppedLastSPSFrameOut); + bool *overrecursed); uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 8609a3a56a0b..f5b29abd9e89 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -551,16 +551,13 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, HandleFunction fun, HandleScript script, IonScript *ionScript, SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder, AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee, - jsbytecode **callPC, const ExceptionBailoutInfo *excInfo, - bool *poppedLastSPSFrameOut) + jsbytecode **callPC, const ExceptionBailoutInfo *excInfo) { // The Baseline frames we will reconstruct on the heap are not rooted, so GC // must be suppressed here. MOZ_ASSERT(cx->mainThread().suppressGC); MOZ_ASSERT(script->hasBaselineScript()); - MOZ_ASSERT(poppedLastSPSFrameOut); - MOZ_ASSERT(!*poppedLastSPSFrameOut); // Are we catching an exception? bool catchingException = excInfo && excInfo->catchingException(); @@ -626,16 +623,6 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, uint32_t flags = 0; - // If SPS Profiler is enabled, mark the frame as having pushed an SPS entry. - // This may be wrong for the last frame of ArgumentCheck bailout, but - // that will be fixed later. - if (ionScript->hasSPSInstrumentation()) { - if (callerPC == nullptr) { - JitSpew(JitSpew_BaselineBailouts, " Setting SPS flag on top frame!"); - flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME; - } - } - // If we are bailing to a script whose execution is observed, mark the // baseline frame as a debuggee frame. This is to cover the case where we // don't rematerialize the Ion frame via the Debugger. @@ -1098,35 +1085,6 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, opReturnAddr = baselineScript->prologueEntryAddr(); JitSpew(JitSpew_BaselineBailouts, " Resuming into prologue."); - // If bailing into prologue, HAS_PUSHED_SPS_FRAME should not be set on frame. - blFrame->unsetPushedSPSFrame(); - - if (cx->runtime()->spsProfiler.enabled()) { - // 1. If resuming into inline code, then the top SPS entry will be - // for the outermost caller, and will have an uninitialized PC. - // This will be fixed up later in BailoutIonToBaseline. - // - // 2. If resuming into top-level code prologue, with ArgumentCheck, - // no SPS entry will have been pushed. Can be left alone. - // - // 3. If resuming into top-level code prologue, without ArgumentCheck, - // an SPS entry will have been pushed, and needs to be popped. - // - // 4. If resuming into top-level code main body, an SPS entry will - // have been pushed, and can be left alone. - // - // Only need to handle case 3 here. - if (!caller && bailoutKind != Bailout_ArgumentCheck) { - JitSpew(JitSpew_BaselineBailouts, - " Popping SPS entry for outermost frame"); - cx->runtime()->spsProfiler.exit(script, fun); - - // Notify caller that the last SPS frame was popped, so not - // to do it again. - if (poppedLastSPSFrameOut) - *poppedLastSPSFrameOut = true; - } - } } else { opReturnAddr = nativeCodeForPC; } @@ -1135,16 +1093,6 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, } if (cx->runtime()->spsProfiler.enabled()) { - if (blFrame->hasPushedSPSFrame()) { - // Set PC index to 0 for the innermost frame to match what the - // interpreter and Baseline do: they update the SPS pc for - // JSOP_CALL ops but set it to 0 when running other ops. Ion code - // can set the pc to NullPCIndex and this will confuse SPS when - // Baseline calls into the VM at non-CALL ops and re-enters JS. - JitSpew(JitSpew_BaselineBailouts, " Setting PCidx for last frame to 0"); - cx->runtime()->spsProfiler.updatePC(script, script->code()); - } - // Register bailout with profiler. const char *filename = script->filename(); if (filename == nullptr) @@ -1381,14 +1329,11 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, uint32_t jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIterator &iter, bool invalidate, BaselineBailoutInfo **bailoutInfo, - const ExceptionBailoutInfo *excInfo, bool *poppedLastSPSFrameOut) + const ExceptionBailoutInfo *excInfo) { MOZ_ASSERT(bailoutInfo != nullptr); MOZ_ASSERT(*bailoutInfo == nullptr); - MOZ_ASSERT(poppedLastSPSFrameOut); - MOZ_ASSERT(!*poppedLastSPSFrameOut); - TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime()); TraceLogStopEvent(logger, TraceLogger_IonMonkey); TraceLogStartEvent(logger, TraceLogger_Baseline); @@ -1492,9 +1437,6 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIter RootedFunction fun(cx, callee); AutoValueVector startFrameFormals(cx); - RootedScript topCaller(cx); - jsbytecode *topCallerPC = nullptr; - gc::AutoSuppressGC suppress(cx); while (true) { @@ -1524,8 +1466,7 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIter RootedFunction nextCallee(cx, nullptr); if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(), snapIter, invalidate, builder, startFrameFormals, - &nextCallee, &callPC, passExcInfo ? excInfo : nullptr, - poppedLastSPSFrameOut)) + &nextCallee, &callPC, passExcInfo ? excInfo : nullptr)) { return BAILOUT_RETURN_FATAL_ERROR; } @@ -1545,24 +1486,12 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIter fun = nextCallee; scr = fun->existingScriptForInlinedFunction(); - // Save top caller info for adjusting SPS frames later. - if (!topCaller) { - MOZ_ASSERT(frameNo == 0); - topCaller = caller; - topCallerPC = callerPC; - } - frameNo++; snapIter.nextInstruction(); } JitSpew(JitSpew_BaselineBailouts, " Done restoring frames"); - // If there were multiple inline frames unpacked, then the current top SPS frame - // is for the outermost caller, and has an uninitialized PC. Initialize it now. - if (frameNo > 0) - cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC); - BailoutKind bailoutKind = snapIter.bailoutKind(); if (!startFrameFormals.empty()) { diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index f88f1a0a742d..154b871239e8 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -175,7 +175,6 @@ BaselineCompiler::compile() prologueOffset_.fixup(&masm); epilogueOffset_.fixup(&masm); - spsPushToggleOffset_.fixup(&masm); profilerEnterFrameToggleOffset_.fixup(&masm); profilerExitFrameToggleOffset_.fixup(&masm); #ifdef JS_TRACE_LOGGING @@ -190,7 +189,6 @@ BaselineCompiler::compile() mozilla::UniquePtr > baselineScript( BaselineScript::New(script, prologueOffset_.offset(), epilogueOffset_.offset(), - spsPushToggleOffset_.offset(), profilerEnterFrameToggleOffset_.offset(), profilerExitFrameToggleOffset_.offset(), traceLoggerEnterToggleOffset_.offset(), @@ -246,10 +244,6 @@ BaselineCompiler::compile() if (cx->zone()->needsIncrementalBarrier()) baselineScript->toggleBarriers(true); - // All SPS instrumentation is emitted toggled off. Toggle them on if needed. - if (cx->runtime()->spsProfiler.enabled()) - baselineScript->toggleSPS(true); - #ifdef JS_TRACE_LOGGING // Initialize the tracelogger instrumentation. baselineScript->initTraceLogger(cx->runtime(), script); @@ -422,9 +416,6 @@ BaselineCompiler::emitPrologue() if (!emitArgumentTypeChecks()) return false; - if (!emitSPSPush()) - return false; - return true; } @@ -442,9 +433,6 @@ BaselineCompiler::emitEpilogue() return false; #endif - // Pop SPS frame if necessary - emitSPSPop(); - masm.mov(BaselineFrameReg, BaselineStackReg); masm.pop(BaselineFrameReg); @@ -839,35 +827,6 @@ BaselineCompiler::emitTraceLoggerExit() } #endif -bool -BaselineCompiler::emitSPSPush() -{ - // Enter the IC, guarded by a toggled jump (initially disabled). - Label noPush; - CodeOffsetLabel toggleOffset = masm.toggledJump(&noPush); - MOZ_ASSERT(frame.numUnsyncedSlots() == 0); - ICProfiler_Fallback::Compiler compiler(cx); - if (!emitNonOpIC(compiler.getStub(&stubSpace_))) - return false; - masm.bind(&noPush); - - // Store the start offset in the appropriate location. - MOZ_ASSERT(spsPushToggleOffset_.offset() == 0); - spsPushToggleOffset_ = toggleOffset; - return true; -} - -void -BaselineCompiler::emitSPSPop() -{ - // If profiler entry was pushed on this frame, pop it. - Label noPop; - masm.branchTest32(Assembler::Zero, frame.addressOfFlags(), - Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), &noPop); - masm.spsPopFrameSafe(&cx->runtime()->spsProfiler, R1.scratchReg()); - masm.bind(&noPop); -} - void BaselineCompiler::emitProfilerEnterFrame() { diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 75bdfa66e0bf..50d6c4dfe761 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -256,8 +256,6 @@ class BaselineCompiler : public BaselineCompilerSpecific bool emitDebugTrap(); bool emitTraceLoggerEnter(); bool emitTraceLoggerExit(); - bool emitSPSPush(); - void emitSPSPop(); void emitProfilerEnterFrame(); void emitProfilerExitFrame(); diff --git a/js/src/jit/BaselineFrame.cpp b/js/src/jit/BaselineFrame.cpp index eeedb40933f6..29f7367b4d73 100644 --- a/js/src/jit/BaselineFrame.cpp +++ b/js/src/jit/BaselineFrame.cpp @@ -172,19 +172,6 @@ BaselineFrame::initForOsr(InterpreterFrame *fp, uint32_t numStackValues) if (fp->hasReturnValue()) setReturnValue(fp->returnValue()); - // If the interpreter pushed an SPS frame when it entered the function, the - // interpreter will pop it after the OSR trampoline returns. In order for - // the Baseline frame to have its SPS flag set, it must have its own SPS - // frame, which the Baseline code will pop on return. Note that the - // profiler may have been enabled or disabled after the function was entered - // but before OSR. - JSContext *cx = GetJSContextFromJitCode(); - SPSProfiler *p = &(cx->runtime()->spsProfiler); - if (p->enabled()) { - p->enter(fp->script(), fp->maybeFun()); - flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME; - } - frameSize_ = BaselineFrame::FramePointerOffset + BaselineFrame::Size() + numStackValues * sizeof(Value); @@ -195,6 +182,8 @@ BaselineFrame::initForOsr(InterpreterFrame *fp, uint32_t numStackValues) *valueSlot(i) = fp->slots()[i]; if (fp->isDebuggee()) { + JSContext *cx = GetJSContextFromJitCode(); + // For debuggee frames, update any Debugger.Frame objects for the // InterpreterFrame to point to the BaselineFrame. diff --git a/js/src/jit/BaselineFrame.h b/js/src/jit/BaselineFrame.h index 4bb3e0b0dc99..30a42f4719ff 100644 --- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -56,9 +56,6 @@ class BaselineFrame // Eval frame, see the "eval frames" comment. EVAL = 1 << 7, - // Frame has profiler entry pushed. - HAS_PUSHED_SPS_FRAME = 1 << 8, - // Frame has over-recursed on an early check. OVER_RECURSED = 1 << 9, @@ -308,18 +305,6 @@ class BaselineFrame return evalScript_; } - bool hasPushedSPSFrame() const { - return flags_ & HAS_PUSHED_SPS_FRAME; - } - - void setPushedSPSFrame() { - flags_ |= HAS_PUSHED_SPS_FRAME; - } - - void unsetPushedSPSFrame() { - flags_ &= ~HAS_PUSHED_SPS_FRAME; - } - bool overRecursed() const { return flags_ & OVER_RECURSED; } diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 1fba957d3dab..ed4636358630 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -297,11 +297,6 @@ ICStub::trace(JSTracer *trc) MarkTypeObject(trc, &updateStub->type(), "baseline-update-typeobject"); break; } - case ICStub::Profiler_PushFunction: { - ICProfiler_PushFunction *pushFunStub = toProfiler_PushFunction(); - MarkScript(trc, &pushFunStub->script(), "baseline-profilerpushfunction-stub-script"); - break; - } case ICStub::GetName_Global: { ICGetName_Global *globalStub = toGetName_Global(); MarkShape(trc, &globalStub->shape(), "baseline-global-stub-shape"); @@ -723,66 +718,6 @@ ICStubCompiler::leaveStubFrame(MacroAssembler &masm, bool calledIntoIon) EmitLeaveStubFrame(masm, calledIntoIon); } -void -ICStubCompiler::guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip) -{ - // This should only be called from the following stubs. - MOZ_ASSERT(kind == ICStub::Call_Scripted || - kind == ICStub::Call_AnyScripted || - kind == ICStub::Call_Native || - kind == ICStub::Call_ClassHook || - kind == ICStub::Call_ScriptedApplyArray || - kind == ICStub::Call_ScriptedApplyArguments || - kind == ICStub::Call_ScriptedFunCall || - kind == ICStub::GetProp_CallScripted || - kind == ICStub::GetProp_CallNative || - kind == ICStub::GetProp_CallNativePrototype || - kind == ICStub::GetProp_CallDOMProxyNative || - kind == ICStub::GetElem_NativePrototypeCallNative || - kind == ICStub::GetElem_NativePrototypeCallScripted || - kind == ICStub::GetProp_CallDOMProxyWithGenerationNative || - kind == ICStub::GetProp_DOMProxyShadowed || - kind == ICStub::SetProp_CallScripted || - kind == ICStub::SetProp_CallNative); - - // Guard on bit in frame that indicates if the SPS frame was pushed in the first - // place. This code is expected to be called from within a stub that has already - // entered a stub frame. - MOZ_ASSERT(entersStubFrame_); - masm.loadPtr(Address(BaselineFrameReg, 0), scratch); - masm.branchTest32(Assembler::Zero, - Address(scratch, BaselineFrame::reverseOffsetOfFlags()), - Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), - skip); - - // Check if profiling is enabled - uint32_t *enabledAddr = cx->runtime()->spsProfiler.addressOfEnabled(); - masm.branch32(Assembler::Equal, AbsoluteAddress(enabledAddr), Imm32(0), skip); -} - -void -ICStubCompiler::emitProfilingUpdate(MacroAssembler &masm, Register pcIdx, Register scratch, - uint32_t stubPcOffset) -{ - Label skipProfilerUpdate; - - // Check if profiling is enabled. - guardProfilingEnabled(masm, scratch, &skipProfilerUpdate); - - // Update profiling entry before leaving function. - masm.load32(Address(BaselineStubReg, stubPcOffset), pcIdx); - masm.spsUpdatePCIdx(&cx->runtime()->spsProfiler, pcIdx, scratch); - - masm.bind(&skipProfilerUpdate); -} - -void -ICStubCompiler::emitProfilingUpdate(MacroAssembler &masm, GeneralRegisterSet regs, - uint32_t stubPcOffset) -{ - emitProfilingUpdate(masm, regs.takeAny(), regs.takeAny(), stubPcOffset); -} - inline bool ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, ValueOperand val, Register scratch, GeneralRegisterSet saveRegs) @@ -863,16 +798,9 @@ EnsureCanEnterIon(JSContext *cx, ICWarmUpCounter_Fallback *stub, BaselineFrame * if (isLoopEntry) { IonScript *ion = script->ionScript(); - MOZ_ASSERT(cx->runtime()->spsProfiler.enabled() == ion->hasSPSInstrumentation()); + MOZ_ASSERT(cx->runtime()->spsProfiler.enabled() == ion->hasProfilingInstrumentation()); MOZ_ASSERT(ion->osrPc() == pc); - // If the baseline frame's SPS handling doesn't match up with the Ion code's SPS - // handling, don't OSR. - if (frame->hasPushedSPSFrame() != ion->hasSPSInstrumentation()) { - JitSpew(JitSpew_BaselineOSR, " OSR crosses SPS handling boundaries, skipping!"); - return true; - } - JitSpew(JitSpew_BaselineOSR, " OSR possible!"); *jitcodePtr = ion->method()->raw() + ion->osrEntryOffset(); } @@ -1104,99 +1032,6 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler &masm) return true; } -// -// ICProfile_Fallback -// - -static bool -DoProfilerFallback(JSContext *cx, BaselineFrame *frame, ICProfiler_Fallback *stub) -{ - RootedScript script(cx, frame->script()); - RootedFunction func(cx, frame->maybeFun()); - mozilla::DebugOnly icEntry = stub->icEntry(); - - FallbackICSpew(cx, stub, "Profiler"); - - SPSProfiler *profiler = &cx->runtime()->spsProfiler; - - // Manually enter SPS this time. - MOZ_ASSERT(profiler->enabled()); - if (!cx->runtime()->spsProfiler.enter(script, func)) - return false; - frame->setPushedSPSFrame(); - - // Unlink any existing PushFunction stub (which may hold stale 'const char *' to - // the profile string. - MOZ_ASSERT_IF(icEntry->firstStub() != stub, - icEntry->firstStub()->isProfiler_PushFunction() && - icEntry->firstStub()->next() == stub); - stub->unlinkStubsWithKind(cx, ICStub::Profiler_PushFunction); - MOZ_ASSERT(icEntry->firstStub() == stub); - - // Generate the string to use to identify this stack frame. - const char *string = profiler->profileString(script, func); - if (string == nullptr) - return false; - - JitSpew(JitSpew_BaselineIC, " Generating Profiler_PushFunction stub for %s:%d", - script->filename(), script->lineno()); - - // Create a new optimized stub. - ICProfiler_PushFunction::Compiler compiler(cx, string, script); - ICStub *optStub = compiler.getStub(compiler.getStubSpace(script)); - if (!optStub) - return false; - stub->addNewStub(optStub); - - return true; -} - -typedef bool (*DoProfilerFallbackFn)(JSContext *, BaselineFrame *frame, ICProfiler_Fallback *); -static const VMFunction DoProfilerFallbackInfo = - FunctionInfo(DoProfilerFallback, TailCall); - -bool -ICProfiler_Fallback::Compiler::generateStubCode(MacroAssembler &masm) -{ - EmitRestoreTailCallReg(masm); - - masm.push(BaselineStubReg); // Push stub. - masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); // Push frame. - - return tailCallVM(DoProfilerFallbackInfo, masm); -} - -bool -ICProfiler_PushFunction::Compiler::generateStubCode(MacroAssembler &masm) -{ - - Register scratch = R0.scratchReg(); - Register scratch2 = R1.scratchReg(); - - // Profiling should be enabled if we ever reach here. -#ifdef DEBUG - Label spsEnabled; - uint32_t *enabledAddr = cx->runtime()->spsProfiler.addressOfEnabled(); - masm.branch32(Assembler::NotEqual, AbsoluteAddress(enabledAddr), Imm32(0), &spsEnabled); - masm.assumeUnreachable("Profiling should have been enabled."); - masm.bind(&spsEnabled); -#endif - - // Push SPS entry. - masm.spsPushFrame(&cx->runtime()->spsProfiler, - Address(BaselineStubReg, ICProfiler_PushFunction::offsetOfStr()), - Address(BaselineStubReg, ICProfiler_PushFunction::offsetOfScript()), - scratch, - scratch2); - - // Mark frame as having profiler entry pushed. - Address flagsOffset(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()); - masm.or32(Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), flagsOffset); - - EmitReturnFromIC(masm); - - return true; -} // // TypeMonitor_Fallback @@ -4229,9 +4064,6 @@ ICGetElemNativeCompiler::emitCallNative(MacroAssembler &masm, Register objReg) regs.add(objReg); - // Profiler hook. - emitProfilingUpdate(masm, regs, ICGetElemNativeGetterStub::offsetOfPCOffset()); - // Call helper. if (!callVM(DoCallNativeGetterInfo, masm)) return false; @@ -4297,16 +4129,6 @@ ICGetElemNativeCompiler::emitCallScripted(MacroAssembler &masm, Register objReg) } masm.bind(&noUnderflow); - - // If needed, update SPS Profiler frame entry. At this point, callee and scratch can - // be clobbered. - { - GeneralRegisterSet availRegs = availableGeneralRegs(0); - availRegs.take(ArgumentsRectifierReg); - availRegs.take(code); - emitProfilingUpdate(masm, availRegs, ICGetElemNativeGetterStub::offsetOfPCOffset()); - } - masm.callJit(code); leaveStubFrame(masm, true); @@ -7463,16 +7285,6 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm) } masm.bind(&noUnderflow); - - // If needed, update SPS Profiler frame entry. At this point, callee and scratch can - // be clobbered. - { - GeneralRegisterSet availRegs = availableGeneralRegs(0); - availRegs.take(ArgumentsRectifierReg); - availRegs.take(code); - emitProfilingUpdate(masm, availRegs, ICGetProp_CallScripted::offsetOfPCOffset()); - } - masm.callJit(code); leaveStubFrame(masm, true); @@ -7534,9 +7346,6 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm) if (!inputDefinitelyObject_) regs.add(R0); - // If needed, update SPS Profiler frame entry. - emitProfilingUpdate(masm, regs, ICGetProp_CallNative::offsetOfPCOffset()); - if (!callVM(DoCallNativeGetterInfo, masm)) return false; leaveStubFrame(masm); @@ -7606,9 +7415,6 @@ ICGetProp_CallNativePrototype::Compiler::generateStubCode(MacroAssembler &masm) else regs.add(objReg); - // If needed, update SPS Profiler frame entry. - emitProfilingUpdate(masm, regs, ICGetProp_CallNativePrototype::offsetOfPCOffset()); - if (!callVM(DoCallNativeGetterInfo, masm)) return false; leaveStubFrame(masm); @@ -7679,9 +7485,6 @@ ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler &masm, // Don't have to preserve R0 anymore. regs.add(R0); - // If needed, update SPS Profiler frame entry. - emitProfilingUpdate(masm, regs, ICGetProp_CallDOMProxyNative::offsetOfPCOffset()); - if (!callVM(DoCallNativeGetterInfo, masm)) return false; leaveStubFrame(masm); @@ -7814,9 +7617,6 @@ ICGetProp_DOMProxyShadowed::Compiler::generateStubCode(MacroAssembler &masm) // Don't have to preserve R0 anymore. regs.add(R0); - // If needed, update SPS Profiler frame entry. - emitProfilingUpdate(masm, regs, ICGetProp_DOMProxyShadowed::offsetOfPCOffset()); - if (!callVM(ProxyGetInfo, masm)) return false; leaveStubFrame(masm); @@ -8840,16 +8640,6 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm) } masm.bind(&noUnderflow); - - // If needed, update SPS Profiler frame entry. At this point, callee and scratch can - // be clobbered. - { - GeneralRegisterSet availRegs = availableGeneralRegs(0); - availRegs.take(ArgumentsRectifierReg); - availRegs.take(code); - emitProfilingUpdate(masm, availRegs, ICSetProp_CallScripted::offsetOfPCOffset()); - } - masm.callJit(code); leaveStubFrame(masm, true); @@ -8935,9 +8725,6 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm) // Don't need to preserve R0 anymore. regs.add(R0); - // If needed, update SPS Profiler frame entry. - emitProfilingUpdate(masm, regs, ICSetProp_CallNative::offsetOfPCOffset()); - if (!callVM(DoCallNativeSetterInfo, masm)) return false; leaveStubFrame(masm); @@ -9486,10 +9273,6 @@ DoCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub_, uint if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false, newType)) return false; - // Maybe update PC in profiler entry before leaving this script by call. - if (cx->runtime()->spsProfiler.enabled() && frame->hasPushedSPSFrame()) - cx->runtime()->spsProfiler.updatePC(script, pc); - if (!MaybeCloneFunctionAtCallsite(cx, &callee, script, pc)) return false; @@ -9562,10 +9345,6 @@ DoSpreadCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub_ return false; } - // Maybe update PC in profiler entry before leaving this script by call. - if (cx->runtime()->spsProfiler.enabled() && frame->hasPushedSPSFrame()) - cx->runtime()->spsProfiler.updatePC(script, pc); - if (!MaybeCloneFunctionAtCallsite(cx, &callee, script, pc)) return false; @@ -10145,18 +9924,6 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler &masm) } masm.bind(&noUnderflow); - - // If needed, update SPS Profiler frame entry before and after call. - { - MOZ_ASSERT(kind == ICStub::Call_Scripted || kind == ICStub::Call_AnyScripted); - GeneralRegisterSet availRegs = availableGeneralRegs(0); - availRegs.take(ArgumentsRectifierReg); - availRegs.take(code); - emitProfilingUpdate(masm, availRegs, kind == ICStub::Call_Scripted ? - ICCall_Scripted::offsetOfPCOffset() - : ICCall_AnyScripted::offsetOfPCOffset()); - } - masm.callJit(code); // If this is a constructing call, and the callee returns a non-object, replace it with @@ -10417,10 +10184,6 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler &masm) masm.push(BaselineTailCallReg); masm.enterFakeExitFrame(NativeExitFrameLayout::Token()); - // If needed, update SPS Profiler frame entry. At this point, BaselineTailCallReg - // and scratch can be clobbered. - emitProfilingUpdate(masm, BaselineTailCallReg, scratch, ICCall_Native::offsetOfPCOffset()); - // Execute call. masm.setupUnalignedABICall(3, scratch); masm.loadJSContext(scratch); @@ -10516,10 +10279,6 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler &masm) masm.push(BaselineTailCallReg); masm.enterFakeExitFrame(NativeExitFrameLayout::Token()); - // If needed, update SPS Profiler frame entry. At this point, BaselineTailCallReg - // and scratch can be clobbered. - emitProfilingUpdate(masm, BaselineTailCallReg, scratch, ICCall_ClassHook::offsetOfPCOffset()); - // Execute call. masm.setupUnalignedABICall(3, scratch); masm.loadJSContext(scratch); @@ -10634,11 +10393,6 @@ ICCall_ScriptedApplyArray::Compiler::generateStubCode(MacroAssembler &masm) masm.bind(&noUnderflow); regs.add(argcReg); - // If needed, update SPS Profiler frame entry. At this point, BaselineTailCallReg - // and scratch can be clobbered. - emitProfilingUpdate(masm, regs.getAny(), scratch, - ICCall_ScriptedApplyArguments::offsetOfPCOffset()); - // Do call masm.callJit(target); leaveStubFrame(masm, true); @@ -10735,11 +10489,6 @@ ICCall_ScriptedApplyArguments::Compiler::generateStubCode(MacroAssembler &masm) masm.bind(&noUnderflow); regs.add(argcReg); - // If needed, update SPS Profiler frame entry. At this point, BaselineTailCallReg - // and scratch can be clobbered. - emitProfilingUpdate(masm, regs.getAny(), scratch, - ICCall_ScriptedApplyArguments::offsetOfPCOffset()); - // Do call masm.callJit(target); leaveStubFrame(masm, true); @@ -10856,16 +10605,6 @@ ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler &masm) } masm.bind(&noUnderflow); - - // If needed, update SPS Profiler frame entry. - { - // Need to avoid using ArgumentsRectifierReg and code register. - GeneralRegisterSet availRegs = availableGeneralRegs(0); - availRegs.take(ArgumentsRectifierReg); - availRegs.take(code); - emitProfilingUpdate(masm, availRegs, ICCall_ScriptedFunCall::offsetOfPCOffset()); - } - masm.callJit(code); leaveStubFrame(masm, true); @@ -11518,13 +11257,6 @@ ICRetSub_Resume::Compiler::generateStubCode(MacroAssembler &masm) return true; } -ICProfiler_PushFunction::ICProfiler_PushFunction(JitCode *stubCode, const char *str, - HandleScript script) - : ICStub(ICStub::Profiler_PushFunction, stubCode), - str_(str), - script_(script) -{ } - ICTypeMonitor_SingleObject::ICTypeMonitor_SingleObject(JitCode *stubCode, HandleObject obj) : ICStub(TypeMonitor_SingleObject, stubCode), obj_(obj) diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 60de69df253d..8da23b04f56c 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -335,9 +335,6 @@ class ICEntry #define IC_STUB_KIND_LIST(_) \ _(WarmUpCounter_Fallback) \ \ - _(Profiler_Fallback) \ - _(Profiler_PushFunction) \ - \ _(TypeMonitor_Fallback) \ _(TypeMonitor_SingleObject) \ _(TypeMonitor_TypeObject) \ @@ -1122,11 +1119,6 @@ class ICStubCompiler // given label. void guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip); - // Higher-level helper to emit an update to the profiler pseudo-stack. - void emitProfilingUpdate(MacroAssembler &masm, Register pcIdx, Register scratch, - uint32_t stubPcOffset); - void emitProfilingUpdate(MacroAssembler &masm, GeneralRegisterSet regs, uint32_t stubPcOffset); - inline GeneralRegisterSet availableGeneralRegs(size_t numInputs) const { GeneralRegisterSet regs(GeneralRegisterSet::All()); MOZ_ASSERT(!regs.has(BaselineStackReg)); @@ -1229,91 +1221,6 @@ class ICWarmUpCounter_Fallback : public ICFallbackStub }; }; -// Profiler_Fallback - -class ICProfiler_Fallback : public ICFallbackStub -{ - friend class ICStubSpace; - - explicit ICProfiler_Fallback(JitCode *stubCode) - : ICFallbackStub(ICStub::Profiler_Fallback, stubCode) - { } - - public: - static inline ICProfiler_Fallback *New(ICStubSpace *space, JitCode *code) { - if (!code) - return nullptr; - return space->allocate(code); - } - - // Compiler for this stub kind. - class Compiler : public ICStubCompiler { - protected: - bool generateStubCode(MacroAssembler &masm); - - public: - explicit Compiler(JSContext *cx) - : ICStubCompiler(cx, ICStub::Profiler_Fallback) - { } - - ICProfiler_Fallback *getStub(ICStubSpace *space) { - return ICProfiler_Fallback::New(space, getStubCode()); - } - }; -}; - -// Profiler_PushFunction - -class ICProfiler_PushFunction : public ICStub -{ - friend class ICStubSpace; - - protected: - const char *str_; - HeapPtrScript script_; - - ICProfiler_PushFunction(JitCode *stubCode, const char *str, HandleScript script); - - public: - static inline ICProfiler_PushFunction *New(ICStubSpace *space, JitCode *code, - const char *str, HandleScript script) - { - if (!code) - return nullptr; - return space->allocate(code, str, script); - } - - HeapPtrScript &script() { - return script_; - } - - static size_t offsetOfStr() { - return offsetof(ICProfiler_PushFunction, str_); - } - static size_t offsetOfScript() { - return offsetof(ICProfiler_PushFunction, script_); - } - - // Compiler for this stub kind. - class Compiler : public ICStubCompiler { - protected: - const char *str_; - RootedScript script_; - bool generateStubCode(MacroAssembler &masm); - - public: - Compiler(JSContext *cx, const char *str, HandleScript script) - : ICStubCompiler(cx, ICStub::Profiler_PushFunction), - str_(str), - script_(cx, script) - { } - - ICProfiler_PushFunction *getStub(ICStubSpace *space) { - return ICProfiler_PushFunction::New(space, getStubCode(), str_, script_); - } - }; -}; - // TypeCheckPrimitiveSetStub // Base class for IC stubs (TypeUpdate or TypeMonitor) that check that a given diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 8ed6fc947a0c..2765e393827e 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -42,7 +42,6 @@ PCMappingSlotInfo::ToSlotLocation(const StackValue *stackVal) } BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, - uint32_t spsPushToggleOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, uint32_t traceLoggerEnterToggleOffset, @@ -54,10 +53,6 @@ BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, dependentAsmJSModules_(nullptr), prologueOffset_(prologueOffset), epilogueOffset_(epilogueOffset), -#ifdef DEBUG - spsOn_(false), -#endif - spsPushToggleOffset_(spsPushToggleOffset), profilerEnterToggleOffset_(profilerEnterToggleOffset), profilerExitToggleOffset_(profilerExitToggleOffset), #ifdef JS_TRACE_LOGGING @@ -347,7 +342,6 @@ jit::CanEnterBaselineMethod(JSContext *cx, RunState &state) BaselineScript * BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilogueOffset, - uint32_t spsPushToggleOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, uint32_t traceLoggerEnterToggleOffset, uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset, @@ -377,7 +371,6 @@ BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilog if (!script) return nullptr; new (script) BaselineScript(prologueOffset, epilogueOffset, - spsPushToggleOffset, profilerEnterToggleOffset, profilerExitToggleOffset, traceLoggerEnterToggleOffset, traceLoggerExitToggleOffset, postDebugPrologueOffset); @@ -856,25 +849,6 @@ BaselineScript::toggleDebugTraps(JSScript *script, jsbytecode *pc) } } -void -BaselineScript::toggleSPS(bool enable) -{ - MOZ_ASSERT(enable == !(bool)spsOn_); - - JitSpew(JitSpew_BaselineIC, " toggling SPS %s for BaselineScript %p", - enable ? "on" : "off", this); - - // Toggle the jump - CodeLocationLabel pushToggleLocation(method_, CodeOffsetLabel(spsPushToggleOffset_)); - if (enable) - Assembler::ToggleToCmp(pushToggleLocation); - else - Assembler::ToggleToJmp(pushToggleLocation); -#ifdef DEBUG - spsOn_ = enable; -#endif -} - #ifdef JS_TRACE_LOGGING void BaselineScript::initTraceLogger(JSRuntime *runtime, JSScript *script) @@ -965,7 +939,7 @@ BaselineScript::toggleProfilerInstrumentation(bool enable) if (enable == isProfilerInstrumentationOn()) return; - JitSpew(JitSpew_BaselineIC, " toggling SPS %s for BaselineScript %p", + JitSpew(JitSpew_BaselineIC, " toggling profiling %s for BaselineScript %p", enable ? "on" : "off", this); // Toggle the jump @@ -1077,14 +1051,13 @@ jit::AddSizeOfBaselineData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf, } void -jit::ToggleBaselineSPS(JSRuntime *runtime, bool enable) +jit::ToggleBaselineProfiling(JSRuntime *runtime, bool enable) { for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); if (!script->hasBaselineScript()) continue; - script->baselineScript()->toggleSPS(enable); script->baselineScript()->toggleProfilerInstrumentation(enable); } } diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 8ffc4be91eaa..b0a5218b9bab 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -139,11 +139,7 @@ struct BaselineScript // returned from. uint32_t epilogueOffset_; - // The offsets for the toggledJump instructions for SPS update ICs. -#ifdef DEBUG - mozilla::DebugOnly spsOn_; -#endif - uint32_t spsPushToggleOffset_; + // The offsets for the toggledJump instructions for profiler instrumentation. uint32_t profilerEnterToggleOffset_; uint32_t profilerExitToggleOffset_; @@ -219,7 +215,6 @@ struct BaselineScript public: // Do not call directly, use BaselineScript::New. This is public for cx->new_. BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, - uint32_t spsPushToggleOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, uint32_t traceLoggerEnterToggleOffset, @@ -228,7 +223,6 @@ struct BaselineScript static BaselineScript *New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilogueOffset, uint32_t postDebugPrologueOffset, - uint32_t spsPushToggleOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, uint32_t traceLoggerEnterToggleOffset, @@ -398,7 +392,6 @@ struct BaselineScript // toggle traps at |pc|. void toggleDebugTraps(JSScript *script, jsbytecode *pc); - void toggleSPS(bool enable); void toggleProfilerInstrumentation(bool enable); bool isProfilerInstrumentationOn() const { return flags_ & PROFILER_INSTRUMENTATION_ON; @@ -464,7 +457,7 @@ AddSizeOfBaselineData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf, size size_t *fallbackStubs); void -ToggleBaselineSPS(JSRuntime *runtime, bool enable); +ToggleBaselineProfiling(JSRuntime *runtime, bool enable); void ToggleBaselineTraceLoggerScripts(JSRuntime *runtime, bool enable); @@ -516,8 +509,7 @@ struct BaselineBailoutInfo uint32_t BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIterator &iter, bool invalidate, BaselineBailoutInfo **bailoutInfo, - const ExceptionBailoutInfo *exceptionInfo, - bool *poppedLastSPSFrame); + const ExceptionBailoutInfo *exceptionInfo); // Mark baseline scripts on the stack as active, so that they are not discarded // during GC. diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 0a6b38eaf7dc..60c82a40f05c 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3653,7 +3653,7 @@ CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mi MOZ_CRASH(); } - masm.callWithABINoProfiling(callee); + masm.callWithABI(callee); restoreVolatile(); masm.bind(&done); @@ -3719,7 +3719,7 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir) masm.loadJSContext(temp2); masm.passABIArg(temp2); masm.passABIArg(temp1); - masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, AssertValidValue)); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, AssertValidValue)); masm.popValue(output); restoreVolatile(); @@ -6932,9 +6932,6 @@ CodeGenerator::generateAsmJS(AsmJSFunctionLabels *labels) { JitSpew(JitSpew_Codegen, "# Emitting asm.js code"); - // AsmJS doesn't do SPS instrumentation. - sps_.disable(); - if (!omitOverRecursedCheck()) labels->overflowThunk.emplace(); @@ -7210,8 +7207,8 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset()); // If SPS is enabled, mark IonScript as having been instrumented with SPS - if (sps_.enabled()) - ionScript->setHasSPSInstrumentation(); + if (isProfilerInstrumentationEnabled()) + ionScript->setHasProfilingInstrumentation(); script->setIonScript(cx, ionScript); @@ -8897,49 +8894,6 @@ CodeGenerator::visitSetDOMProperty(LSetDOMProperty *ins) MOZ_ASSERT(masm.framePushed() == initialStack); } -typedef bool(*SPSFn)(JSContext *, HandleScript); -static const VMFunction SPSEnterInfo = FunctionInfo(SPSEnter); -static const VMFunction SPSExitInfo = FunctionInfo(SPSExit); - -void -CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir) -{ - Register temp = ToRegister(lir->temp()->output()); - - switch (lir->type()) { - case MProfilerStackOp::Enter: - if (gen->options.spsSlowAssertionsEnabled()) { - saveLive(lir); - pushArg(ImmGCPtr(lir->script())); - callVM(SPSEnterInfo, lir); - restoreLive(lir); - sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ false); - } else { - masm.propagateOOM(sps_.push(lir->script(), masm, temp, - /* inlinedFunction = */ false)); - } - return; - - case MProfilerStackOp::Exit: - if (gen->options.spsSlowAssertionsEnabled()) { - saveLive(lir); - pushArg(ImmGCPtr(lir->script())); - // Once we've exited, then we shouldn't emit instrumentation for - // the corresponding reenter() because we no longer have a - // frame. - sps_.skipNextReenter(); - callVM(SPSExitInfo, lir); - restoreLive(lir); - } else { - sps_.pop(masm, temp, /* inlinedFunction = */ false); - } - return; - - default: - MOZ_CRASH("invalid LProfilerStackOp type"); - } -} - class OutOfLineIsCallable : public OutOfLineCodeBase { LIsCallable *ins_; diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 7a625b3cef41..60dacf547459 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -293,7 +293,6 @@ class CodeGenerator : public CodeGeneratorSpecific void visitInstanceOfO(LInstanceOfO *ins); void visitInstanceOfV(LInstanceOfV *ins); void visitCallInstanceOf(LCallInstanceOf *ins); - void visitProfilerStackOp(LProfilerStackOp *lir); void visitGetDOMProperty(LGetDOMProperty *lir); void visitGetDOMMemberV(LGetDOMMemberV *lir); void visitGetDOMMemberT(LGetDOMMemberT *lir); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index c6c228329794..1c2916aecdb5 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -705,7 +705,7 @@ IonScript::IonScript() invalidateEpilogueOffset_(0), invalidateEpilogueDataOffset_(0), numBailouts_(0), - hasSPSInstrumentation_(false), + hasProfilingInstrumentation_(false), recompiling_(false), runtimeData_(0), runtimeSize_(0), diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 3095731da1e6..bddb2aa449c9 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -705,8 +705,6 @@ IonBuilder::build() // Emit the start instruction, so we can begin real instructions. current->add(MStart::New(alloc(), MStart::StartType_Default)); - if (instrumentedProfiling()) - current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Enter)); // Guard against over-recursion. Do this before we start unboxing, since // this will create an OSI point that will read the incoming argument @@ -4072,9 +4070,6 @@ IonBuilder::processReturn(JSOp op) MOZ_CRASH("unknown return op"); } - if (instrumentedProfiling() && inliningDepth_ == 0) { - current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit)); - } MReturn *ret = MReturn::New(alloc(), def); current->end(ret); diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index c7342f63ed83..3791b071203f 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -193,8 +193,8 @@ struct IonScript // Number of times this script bailed out without invalidation. uint32_t numBailouts_; - // Flag set if IonScript was compiled with SPS profiling enabled. - bool hasSPSInstrumentation_; + // Flag set if IonScript was compiled with profiling enabled. + bool hasProfilingInstrumentation_; // Flag for if this script is getting recompiled. uint32_t recompiling_; @@ -412,14 +412,14 @@ struct IonScript bool bailoutExpected() const { return numBailouts_ > 0; } - void setHasSPSInstrumentation() { - hasSPSInstrumentation_ = true; + void setHasProfilingInstrumentation() { + hasProfilingInstrumentation_ = true; } - void clearHasSPSInstrumentation() { - hasSPSInstrumentation_ = false; + void clearHasProfilingInstrumentation() { + hasProfilingInstrumentation_ = false; } - bool hasSPSInstrumentation() const { - return hasSPSInstrumentation_; + bool hasProfilingInstrumentation() const { + return hasProfilingInstrumentation_; } void setTraceLoggerEvent(TraceLoggerEvent &event) { traceLoggerScriptEvent_ = event; diff --git a/js/src/jit/IonInstrumentation.h b/js/src/jit/IonInstrumentation.h index 05c106178e60..93b96ad40e87 100644 --- a/js/src/jit/IonInstrumentation.h +++ b/js/src/jit/IonInstrumentation.h @@ -19,23 +19,12 @@ typedef SPSInstrumentation BaseInstrumentation; class IonInstrumentation : public BaseInstrumentation { - jsbytecode **trackedPc_; - public: IonInstrumentation(SPSProfiler *profiler, jsbytecode **pc) - : BaseInstrumentation(profiler), - trackedPc_(pc) + : BaseInstrumentation(profiler) { MOZ_ASSERT(pc != nullptr); } - - void leave(MacroAssembler &masm, Register reg, bool inlinedFunction = false) { - BaseInstrumentation::leave(*trackedPc_, masm, reg, inlinedFunction); - } - - bool enterInlineFrame() { - return BaseInstrumentation::enterInlineFrame(*trackedPc_); - } }; } // namespace jit diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index cc5245e489ee..15a847be70f7 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -379,7 +379,7 @@ CloseLiveIterator(JSContext *cx, const InlineFrameIterator &frame, uint32_t loca static void HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe, - bool *overrecursed, bool *poppedLastSPSFrameOut) + bool *overrecursed) { RootedScript script(cx, frame.script()); jsbytecode *pc = frame.pc(); @@ -411,8 +411,7 @@ HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromEx // to the stack depth at the snapshot, as we could've thrown in the // middle of a call. ExceptionBailoutInfo propagateInfo; - uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed, - poppedLastSPSFrameOut); + uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed); if (retval == BAILOUT_RETURN_OK) return; } @@ -454,8 +453,7 @@ HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromEx // Bailout at the start of the catch block. jsbytecode *catchPC = script->main() + tn->start + tn->length; ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth); - uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed, - poppedLastSPSFrameOut); + uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed); if (retval == BAILOUT_RETURN_OK) return; @@ -763,8 +761,7 @@ HandleException(ResumeFromException *rfe) bool invalidated = iter.checkInvalidation(&ionScript); for (;;) { - bool poppedLastSPSFrame = false; - HandleExceptionIon(cx, frames, rfe, &overrecursed, &poppedLastSPSFrame); + HandleExceptionIon(cx, frames, rfe, &overrecursed); if (rfe->kind == ResumeFromException::RESUME_BAILOUT) { if (invalidated) @@ -774,29 +771,13 @@ HandleException(ResumeFromException *rfe) MOZ_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME); - // Figure out whether SPS frame was pushed for this frame or not. - // Even if profiler is enabled, the frame being popped might have - // been entered prior to SPS being enabled, and thus not have - // a pushed SPS frame. - bool popSPSFrame = cx->runtime()->spsProfiler.enabled(); - if (invalidated) - popSPSFrame = ionScript->hasSPSInstrumentation(); - - // Don't pop an SPS frame for inlined frames, since they are not instrumented. - if (frames.more()) - popSPSFrame = false; - - // Don't pop the last SPS frame if it's already been popped by - // bailing out. - if (poppedLastSPSFrame) - popSPSFrame = false; - // When profiling, each frame popped needs a notification that // the function has exited, so invoke the probe that a function // is exiting. JSScript *script = frames.script(); - probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); + probes::ExitScript(cx, script, script->functionNonDelazifying(), + /* popSPSFrame = */ false); if (!frames.more()) { TraceLogStopEvent(logger, TraceLogger_IonMonkey); TraceLogStopEvent(logger, TraceLogger_Scripts); @@ -847,11 +828,7 @@ HandleException(ResumeFromException *rfe) // Unwind profiler pseudo-stack JSScript *script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), - iter.baselineFrame()->hasPushedSPSFrame()); - // After this point, any pushed SPS frame would have been popped if it needed - // to be. Unset the flag here so that if we call DebugEpilogue below, - // it doesn't try to pop the SPS frame again. - iter.baselineFrame()->unsetPushedSPSFrame(); + /* popSPSFrame = */ false); if (iter.baselineFrame()->isDebuggee() && !calledDebugEpilogue) { // If we still need to call the DebugEpilogue, we must diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 2d88ab11296b..9bb35c1da27b 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -6165,28 +6165,6 @@ class LCallInstanceOf : public LCallInstructionHelper<1, BOX_PIECES+1, 0> static const size_t RHS = BOX_PIECES; }; -class LProfilerStackOp : public LInstructionHelper<0, 0, 1> -{ - public: - LIR_HEADER(ProfilerStackOp) - - explicit LProfilerStackOp(const LDefinition &temp) { - setTemp(0, temp); - } - - const LDefinition *temp() { - return getTemp(0); - } - - JSScript *script() { - return mir_->toProfilerStackOp()->script(); - } - - MProfilerStackOp::Type type() { - return mir_->toProfilerStackOp()->type(); - } -}; - class LIsCallable : public LInstructionHelper<1, 1, 0> { public: diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 2a50bfbbdbdb..6df9f485a41b 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -305,7 +305,6 @@ _(InterruptCheck) \ _(AsmJSInterruptCheck) \ _(InterruptCheckImplicit) \ - _(ProfilerStackOp) \ _(GetDOMProperty) \ _(GetDOMMemberV) \ _(GetDOMMemberT) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 7d9d705f193d..3cb751eb247e 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3492,18 +3492,6 @@ LIRGenerator::visitCallInstanceOf(MCallInstanceOf *ins) assignSafepoint(lir, ins); } -void -LIRGenerator::visitProfilerStackOp(MProfilerStackOp *ins) -{ - LProfilerStackOp *lir = new(alloc()) LProfilerStackOp(temp()); - add(lir, ins); - - // If slow assertions are enabled, then this node will result in a callVM - // out to a C++ function for the assertions, so we will need a safepoint. - if (gen->options.spsSlowAssertionsEnabled()) - assignSafepoint(lir, ins); -} - void LIRGenerator::visitIsCallable(MIsCallable *ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 7c166bdb7fd8..d53fd2a2dabc 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -248,7 +248,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitInArray(MInArray *ins); void visitInstanceOf(MInstanceOf *ins); void visitCallInstanceOf(MCallInstanceOf *ins); - void visitProfilerStackOp(MProfilerStackOp *ins); void visitIsCallable(MIsCallable *ins); void visitIsObject(MIsObject *ins); void visitHasClass(MHasClass *ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 3f82ea68a375..3d726577aee5 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -11449,48 +11449,6 @@ class MNewStringObject : StringObject *templateObj() const; }; -// Node that represents that a script has begun executing. This comes at the -// start of the function and is called once per function (including inline -// ones) -class MProfilerStackOp : public MNullaryInstruction -{ - public: - enum Type { - Enter, // a function has begun executing and it is not inline - Exit // any function has exited and is not inline - }; - - private: - JSScript *script_; - Type type_; - - MProfilerStackOp(JSScript *script, Type type) - : script_(script), type_(type) - { - MOZ_ASSERT(script); - setGuard(); - } - - public: - INSTRUCTION_HEADER(ProfilerStackOp) - - static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type) { - return new(alloc) MProfilerStackOp(script, type); - } - - JSScript *script() { - return script_; - } - - Type type() { - return type_; - } - - AliasSet getAliasSet() const MOZ_OVERRIDE { - return AliasSet::None(); - } -}; - // This is an alias for MLoadFixedSlot. class MEnclosingScope : public MLoadFixedSlot { diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index fd02ef482486..3cb83462d86d 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -226,7 +226,6 @@ namespace jit { _(CallInstanceOf) \ _(InterruptCheck) \ _(AsmJSInterruptCheck) \ - _(ProfilerStackOp) \ _(GetDOMProperty) \ _(GetDOMMember) \ _(SetDOMProperty) \ diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index f3328838885f..5301dda5a7d3 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1314,16 +1314,8 @@ MacroAssembler::handleFailure() { // Re-entry code is irrelevant because the exception will leave the // running function and never come back - if (sps_) - sps_->skipNextReenter(); - leaveSPSFrame(); - JitCode *excTail = GetJitContext()->runtime->jitRuntime()->getExceptionTail(); jump(excTail); - - // Doesn't actually emit code, but balances the leave() - if (sps_) - sps_->reenter(*this, InvalidReg); } #ifdef DEBUG @@ -1345,7 +1337,7 @@ MacroAssembler::assumeUnreachable(const char *output) setupUnalignedABICall(1, temp); movePtr(ImmPtr(output), temp); passABIArg(temp); - callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, AssumeUnreachable_)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, AssumeUnreachable_)); PopRegsInMask(RegisterSet::Volatile()); } @@ -1373,7 +1365,7 @@ MacroAssembler::printf(const char *output) setupUnalignedABICall(1, temp); movePtr(ImmPtr(output), temp); passABIArg(temp); - callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, Printf0_)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, Printf0_)); PopRegsInMask(RegisterSet::Volatile()); } @@ -1399,7 +1391,7 @@ MacroAssembler::printf(const char *output, Register value) movePtr(ImmPtr(output), temp); passABIArg(temp); passABIArg(value); - callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, Printf1_)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, Printf1_)); PopRegsInMask(RegisterSet::Volatile()); } @@ -1422,7 +1414,7 @@ MacroAssembler::tracelogStartId(Register logger, uint32_t textId, bool force) passABIArg(logger); move32(Imm32(textId), temp); passABIArg(temp); - callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStartEventPrivate)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLogStartEventPrivate)); PopRegsInMask(RegisterSet::Volatile()); } @@ -1441,7 +1433,7 @@ MacroAssembler::tracelogStartId(Register logger, Register textId) setupUnalignedABICall(2, temp); passABIArg(logger); passABIArg(textId); - callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStartEventPrivate)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLogStartEventPrivate)); PopRegsInMask(RegisterSet::Volatile()); } @@ -1462,7 +1454,7 @@ MacroAssembler::tracelogStartEvent(Register logger, Register event) setupUnalignedABICall(2, temp); passABIArg(logger); passABIArg(event); - callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc)); PopRegsInMask(RegisterSet::Volatile()); } @@ -1485,7 +1477,7 @@ MacroAssembler::tracelogStopId(Register logger, uint32_t textId, bool force) move32(Imm32(textId), temp); passABIArg(temp); - callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStopEventPrivate)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLogStopEventPrivate)); PopRegsInMask(RegisterSet::Volatile()); } @@ -1504,7 +1496,7 @@ MacroAssembler::tracelogStopId(Register logger, Register textId) setupUnalignedABICall(2, temp); passABIArg(logger); passABIArg(textId); - callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStopEventPrivate)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLogStopEventPrivate)); PopRegsInMask(RegisterSet::Volatile()); } @@ -2023,58 +2015,6 @@ MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, Reg } } - -// If a pseudostack frame has this as its label, its stack pointer -// field points to the registers saved on entry to JIT code. A native -// stack unwinder could use that information to continue unwinding -// past that point. -const char MacroAssembler::enterJitLabel[] = "EnterJIT"; - -// Creates an enterJIT pseudostack frame, as described above. Pushes -// a word to the stack to indicate whether this was done. |framePtr| is -// the pointer to the machine-dependent saved state. -void -MacroAssembler::spsMarkJit(SPSProfiler *p, Register framePtr, Register temp) -{ - Label spsNotEnabled; - uint32_t *enabledAddr = p->addressOfEnabled(); - load32(AbsoluteAddress(enabledAddr), temp); - push(temp); // +4: Did we push an sps frame. - branchTest32(Assembler::Equal, temp, temp, &spsNotEnabled); - - Label stackFull; - // We always need the "safe" versions, because these are used in trampolines - // and won't be regenerated when SPS state changes. - spsProfileEntryAddressSafe(p, 0, temp, &stackFull); - - // Push a C++ frame with non-copy label - storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfLabel())); - storePtr(framePtr, Address(temp, ProfileEntry::offsetOfSpOrScript())); - store32(Imm32(0), Address(temp, ProfileEntry::offsetOfLineOrPc())); - store32(Imm32(ProfileEntry::IS_CPP_ENTRY), Address(temp, ProfileEntry::offsetOfFlags())); - - /* Always increment the stack size, whether or not we actually pushed. */ - bind(&stackFull); - loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp); - add32(Imm32(1), Address(temp, 0)); - - bind(&spsNotEnabled); -} - -// Pops the word pushed by spsMarkJit and, if spsMarkJit pushed an SPS -// frame, pops it. -void -MacroAssembler::spsUnmarkJit(SPSProfiler *p, Register temp) -{ - Label spsNotEnabled; - pop(temp); // -4: Was the profiler enabled. - branchTest32(Assembler::Equal, temp, temp, &spsNotEnabled); - - spsPopFrameSafe(p, temp); - - bind(&spsNotEnabled); -} - void MacroAssembler::profilerPreCallImpl() { diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index ead3ffabdf80..782b217a876d 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -185,27 +185,18 @@ class MacroAssembler : public MacroAssemblerSpecific mozilla::Maybe jitContext_; mozilla::Maybe alloc_; - // SPS instrumentation, only used for Ion caches. - mozilla::Maybe spsInstrumentation_; - jsbytecode *spsPc_; - private: // This field is used to manage profiling instrumentation output. If // provided and enabled, then instrumentation will be emitted around call - // sites. The IonInstrumentation instance is hosted inside of - // CodeGeneratorShared and is the manager of when instrumentation is - // actually emitted or not. If nullptr, then no instrumentation is emitted. - IonInstrumentation *sps_; + // sites. + bool emitProfilingInstrumentation_; // Labels for handling exceptions and failures. NonAssertingLabel failureLabel_; public: - // If instrumentation should be emitted, then the sps parameter should be - // provided, but otherwise it can be safely omitted to prevent all - // instrumentation from being emitted. MacroAssembler() - : sps_(nullptr) + : emitProfilingInstrumentation_(false) { JitContext *jcx = GetJitContext(); JSContext *cx = jcx->cx; @@ -228,7 +219,7 @@ class MacroAssembler : public MacroAssemblerSpecific // (for example, Trampoline-$(ARCH).cpp and IonCaches.cpp). explicit MacroAssembler(JSContext *cx, IonScript *ion = nullptr, JSScript *script = nullptr, jsbytecode *pc = nullptr) - : sps_(nullptr) + : emitProfilingInstrumentation_(false) { constructRoot(cx); jitContext_.emplace(cx, (js::jit::TempAllocator *)nullptr); @@ -240,21 +231,15 @@ class MacroAssembler : public MacroAssemblerSpecific #endif if (ion) { setFramePushed(ion->frameSize()); - if (pc && cx->runtime()->spsProfiler.enabled()) { - // We have to update the SPS pc when this IC stub calls into - // the VM. - spsPc_ = pc; - spsInstrumentation_.emplace(&cx->runtime()->spsProfiler, &spsPc_); - sps_ = spsInstrumentation_.ptr(); - sps_->setPushed(script); - } + if (pc && cx->runtime()->spsProfiler.enabled()) + emitProfilingInstrumentation_ = true; } } // asm.js compilation handles its own JitContext-pushing struct AsmJSToken {}; explicit MacroAssembler(AsmJSToken) - : sps_(nullptr) + : emitProfilingInstrumentation_(false) { #ifdef JS_CODEGEN_ARM initWithAllocator(); @@ -262,8 +247,8 @@ class MacroAssembler : public MacroAssemblerSpecific #endif } - void setInstrumentation(IonInstrumentation *sps) { - sps_ = sps; + void enableProfilingInstrumentation() { + emitProfilingInstrumentation_ = true; } void resetForNewCodeGenerator(TempAllocator &alloc) { @@ -874,51 +859,46 @@ class MacroAssembler : public MacroAssemblerSpecific // they are returning the offset of the assembler just after the call has // been made so that a safepoint can be made at that location. - template - void callWithABINoProfiling(const T &fun, MoveOp::Type result = MoveOp::GENERAL) { - MacroAssemblerSpecific::callWithABI(fun, result); - } - template void callWithABI(const T &fun, MoveOp::Type result = MoveOp::GENERAL) { - leaveSPSFrame(); - callWithABINoProfiling(fun, result); - reenterSPSFrame(); + profilerPreCall(); + MacroAssemblerSpecific::callWithABI(fun, result); + profilerPostReturn(); } // see above comment for what is returned uint32_t callJit(Register callee) { - leaveSPSFrame(); + profilerPreCall(); MacroAssemblerSpecific::callJit(callee); uint32_t ret = currentOffset(); - reenterSPSFrame(); + profilerPostReturn(); return ret; } // see above comment for what is returned uint32_t callWithExitFrame(Label *target) { - leaveSPSFrame(); + profilerPreCall(); MacroAssemblerSpecific::callWithExitFrame(target); uint32_t ret = currentOffset(); - reenterSPSFrame(); + profilerPostReturn(); return ret; } // see above comment for what is returned uint32_t callWithExitFrame(JitCode *target) { - leaveSPSFrame(); + profilerPreCall(); MacroAssemblerSpecific::callWithExitFrame(target); uint32_t ret = currentOffset(); - reenterSPSFrame(); + profilerPostReturn(); return ret; } // see above comment for what is returned uint32_t callWithExitFrame(JitCode *target, Register dynStack) { - leaveSPSFrame(); + profilerPreCall(); MacroAssemblerSpecific::callWithExitFrame(target, dynStack); uint32_t ret = currentOffset(); - reenterSPSFrame(); + profilerPostReturn(); return ret; } @@ -954,165 +934,19 @@ class MacroAssembler : public MacroAssemblerSpecific // These two functions are helpers used around call sites throughout the // assembler. They are called from the above call wrappers to emit the // necessary instrumentation. - void leaveSPSFrame() { - if (!sps_ || !sps_->enabled()) + void profilerPreCall() { + if (!emitProfilingInstrumentation_) return; - // No registers are guaranteed to be available, so push/pop a register - // so we can use one - push(CallTempReg0); - sps_->leave(*this, CallTempReg0); - pop(CallTempReg0); + profilerPreCallImpl(); } - void reenterSPSFrame() { - if (!sps_ || !sps_->enabled()) + void profilerPostReturn() { + if (!emitProfilingInstrumentation_) return; - // Attempt to use a now-free register within a given set, but if the - // architecture being built doesn't have an available register, resort - // to push/pop - GeneralRegisterSet regs(Registers::TempMask & ~Registers::JSCallMask & - ~Registers::CallMask); - if (regs.empty()) { - push(CallTempReg0); - sps_->reenter(*this, CallTempReg0); - pop(CallTempReg0); - } else { - sps_->reenter(*this, regs.getAny()); - } - } - - void spsProfileEntryAddress(SPSProfiler *p, int offset, Register temp, - Label *full) - { - movePtr(ImmPtr(p->sizePointer()), temp); - load32(Address(temp, 0), temp); - if (offset != 0) - add32(Imm32(offset), temp); - branch32(Assembler::GreaterThanOrEqual, temp, Imm32(p->maxSize()), full); - - JS_STATIC_ASSERT(sizeof(ProfileEntry) == (2 * sizeof(void *)) + 8); - if (sizeof(void *) == 4) { - lshiftPtr(Imm32(4), temp); - } else { - lshiftPtr(Imm32(3), temp); - mulBy3(temp, temp); - } - - addPtr(ImmPtr(p->stack()), temp); - } - - // The safe version of the above method refrains from assuming that the fields - // of the SPSProfiler class are going to stay the same across different runs of - // the jitcode. Ion can use the more efficient unsafe version because ion jitcode - // will not survive changes to to the profiler settings. Baseline jitcode, however, - // can span these changes, so any hardcoded field values will be incorrect afterwards. - // All the sps-related methods used by baseline call |spsProfileEntryAddressSafe|. - void spsProfileEntryAddressSafe(SPSProfiler *p, int offset, Register temp, - Label *full) - { - // Load size pointer - loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp); - - // Load size - load32(Address(temp, 0), temp); - if (offset != 0) - add32(Imm32(offset), temp); - - // Test against max size. - branch32(Assembler::LessThanOrEqual, AbsoluteAddress(p->addressOfMaxSize()), temp, full); - - JS_STATIC_ASSERT(sizeof(ProfileEntry) == (2 * sizeof(void *)) + 8); - if (sizeof(void *) == 4) { - lshiftPtr(Imm32(4), temp); - } else { - lshiftPtr(Imm32(3), temp); - mulBy3(temp, temp); - } - - push(temp); - loadPtr(AbsoluteAddress(p->addressOfStack()), temp); - addPtr(Address(StackPointer, 0), temp); - addPtr(Imm32(sizeof(size_t)), StackPointer); + profilerPostReturnImpl(); } public: - // These functions are needed by the IonInstrumentation interface defined in - // vm/SPSProfiler.h. They will modify the pseudostack provided to SPS to - // perform the actual instrumentation. - - void spsUpdatePCIdx(SPSProfiler *p, int32_t idx, Register temp) { - Label stackFull; - spsProfileEntryAddress(p, -1, temp, &stackFull); - store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfLineOrPc())); - bind(&stackFull); - } - - void spsUpdatePCIdx(SPSProfiler *p, Register idx, Register temp) { - Label stackFull; - spsProfileEntryAddressSafe(p, -1, temp, &stackFull); - store32(idx, Address(temp, ProfileEntry::offsetOfLineOrPc())); - bind(&stackFull); - } - - // spsPushFrame variant for Ion-optimized scripts. - void spsPushFrame(SPSProfiler *p, const char *str, JSScript *s, Register temp) { - Label stackFull; - spsProfileEntryAddress(p, 0, temp, &stackFull); - - // Push a JS frame with a copy label - storePtr(ImmPtr(str), Address(temp, ProfileEntry::offsetOfLabel())); - storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfSpOrScript())); - store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc())); - store32(Imm32(ProfileEntry::FRAME_LABEL_COPY), Address(temp, ProfileEntry::offsetOfFlags())); - - /* Always increment the stack size, whether or not we actually pushed. */ - bind(&stackFull); - movePtr(ImmPtr(p->sizePointer()), temp); - add32(Imm32(1), Address(temp, 0)); - } - - // spsPushFrame variant for Baseline-optimized scripts. - void spsPushFrame(SPSProfiler *p, const Address &str, const Address &script, - Register temp, Register temp2) - { - Label stackFull; - spsProfileEntryAddressSafe(p, 0, temp, &stackFull); - - // Push a JS frame with a copy label - loadPtr(str, temp2); - storePtr(temp2, Address(temp, ProfileEntry::offsetOfLabel())); - - loadPtr(script, temp2); - storePtr(temp2, Address(temp, ProfileEntry::offsetOfSpOrScript())); - - // Store 0 for PCIdx because that's what interpreter does. - // (See probes::EnterScript, which calls spsProfiler.enter, which pushes an entry - // with 0 pcIdx). - store32(Imm32(0), Address(temp, ProfileEntry::offsetOfLineOrPc())); - store32(Imm32(ProfileEntry::FRAME_LABEL_COPY), Address(temp, ProfileEntry::offsetOfFlags())); - - /* Always increment the stack size, whether or not we actually pushed. */ - bind(&stackFull); - movePtr(ImmPtr(p->addressOfSizePointer()), temp); - loadPtr(Address(temp, 0), temp); - add32(Imm32(1), Address(temp, 0)); - } - - void spsPopFrame(SPSProfiler *p, Register temp) { - movePtr(ImmPtr(p->sizePointer()), temp); - add32(Imm32(-1), Address(temp, 0)); - } - - // spsPropFrameSafe does not assume |profiler->sizePointer()| will stay constant. - void spsPopFrameSafe(SPSProfiler *p, Register temp) { - loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp); - add32(Imm32(-1), Address(temp, 0)); - } - - static const char enterJitLabel[]; - void spsMarkJit(SPSProfiler *p, Register framePtr, Register temp); - void spsUnmarkJit(SPSProfiler *p, Register temp); - void loadBaselineOrIonRaw(Register script, Register dest, Label *failure); void loadBaselineOrIonNoArgCheck(Register callee, Register dest, Label *failure); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 5884d1defc62..8b59c09bef56 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -576,19 +576,6 @@ NewStringObject(JSContext *cx, HandleString str) return StringObject::create(cx, str); } -bool -SPSEnter(JSContext *cx, HandleScript script) -{ - return cx->runtime()->spsProfiler.enter(script, script->functionNonDelazifying()); -} - -bool -SPSExit(JSContext *cx, HandleScript script) -{ - cx->runtime()->spsProfiler.exit(script, script->functionNonDelazifying()); - return true; -} - bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out) { @@ -803,15 +790,6 @@ DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok) DebugScopes::onPopStrictEvalScope(frame); } - // If the frame has a pushed SPS frame, make sure to pop it. - if (frame->hasPushedSPSFrame()) { - cx->runtime()->spsProfiler.exit(frame->script(), frame->maybeFun()); - // Unset the pushedSPSFrame flag because DebugEpilogue may get called before - // probes::ExitScript in baseline during exception handling, and we don't - // want to double-pop SPS frames. - frame->unsetPushedSPSFrame(); - } - if (!ok) { // Pop this frame by updating jitTop, so that the exception handling // code will start at the previous frame. diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 7252bff73447..8f0164d47d97 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -679,9 +679,6 @@ JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, JSObject *NewSingletonCallObject(JSContext *cx, HandleShape shape, uint32_t lexicalBegin); JSObject *NewStringObject(JSContext *cx, HandleString str); -bool SPSEnter(JSContext *cx, HandleScript script); -bool SPSExit(JSContext *cx, HandleScript script); - bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out); bool OperatorInI(JSContext *cx, uint32_t index, HandleObject obj, bool *out); diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index ccc9740b63f5..c2394fc8106d 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -36,8 +36,8 @@ GenerateReturn(MacroAssembler &masm, int returnCode, SPSProfiler *prof) // Restore non-volatile floating point registers. masm.transferMultipleByRuns(NonVolatileFloatRegs, IsLoad, StackPointer, IA); - // Unwind the sps mark. - masm.spsUnmarkJit(prof, r8); + // Get rid of padding word. + masm.addPtr(Imm32(sizeof(void*)), sp); // Set up return value masm.ma_mov(Imm32(returnCode), r0); @@ -69,7 +69,8 @@ struct EnterJITStack double d14; double d15; - size_t hasSPSMark; + // Padding. + void *padding; // Non-volatile registers. void *r4; @@ -129,9 +130,8 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) // The 5th argument is located at [sp, 36] masm.finishDataTransfer(); - // Push the EnterJIT sps mark. "Frame pointer" = start of saved core regs. - masm.movePtr(sp, r8); - masm.spsMarkJit(&cx->runtime()->spsProfiler, r8, r9); + // Add padding word. + masm.subPtr(Imm32(sizeof(void*)), sp); // Push the float registers. masm.transferMultipleByRuns(NonVolatileFloatRegs, IsStore, sp, DB); diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 4d9f50f805ec..68647c8984df 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -59,7 +59,6 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, Mac nativeToBytecodeNumRegions_(0), nativeToBytecodeScriptList_(nullptr), nativeToBytecodeScriptListLength_(0), - sps_(&GetJitContext()->runtime->spsProfiler(), &lastNotInlinedPC_), osrEntryOffset_(0), skipArgCheckEntryOffset_(0), #ifdef CHECK_OSIPOINT_REGISTERS @@ -68,8 +67,8 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, Mac frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()), frameInitialAdjustment_(0) { - if (!gen->compilingAsmJS()) - masm.setInstrumentation(&sps_); + if (gen->isProfilerInstrumentationEnabled()) + masm.enableProfilingInstrumentation(); if (gen->compilingAsmJS()) { // Since asm.js uses the system ABI which does not necessarily use a @@ -107,7 +106,6 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, Mac bool CodeGeneratorShared::generateOutOfLineCode() { - JSScript *topScript = sps_.getPushed(); for (size_t i = 0; i < outOfLineCode_.length(); i++) { // Add native => bytecode mapping entries for OOL sites. // Not enabled on asm.js yet since asm doesn't contain bytecode mappings. @@ -123,16 +121,11 @@ CodeGeneratorShared::generateOutOfLineCode() masm.setFramePushed(outOfLineCode_[i]->framePushed()); lastPC_ = outOfLineCode_[i]->pc(); - if (!sps_.prepareForOOL()) - return false; - sps_.setPushed(outOfLineCode_[i]->script()); outOfLineCode_[i]->bind(&masm); oolIns = outOfLineCode_[i]; outOfLineCode_[i]->generate(this); - sps_.finishOOL(); } - sps_.setPushed(topScript); oolIns = nullptr; return true; diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index 9c41212259ca..a2255d8cf992 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -113,11 +113,6 @@ class CodeGeneratorShared : public LElementVisitor JSScript **nativeToBytecodeScriptList_; uint32_t nativeToBytecodeScriptListLength_; - // When profiling is enabled, this is the instrumentation manager which - // maintains state of what script is currently being generated (for inline - // scripts) and when instrumentation needs to be emitted or skipped. - IonInstrumentation sps_; - bool isProfilerInstrumentationEnabled() { return gen->isProfilerInstrumentationEnabled(); } diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index 41bbff07c70c..a9e0a143a73a 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -81,9 +81,6 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) masm.vmovdqa(xmm15, Operand(rsp, 16 * 9)); #endif - // Push the EnterJIT sps mark. - masm.spsMarkJit(&cx->runtime()->spsProfiler, rbp, rbx); - // Save arguments passed in registers needed after function call. masm.push(result); @@ -281,9 +278,6 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) masm.pop(r12); // vp masm.storeValue(JSReturnOperand, Operand(r12, 0)); - // Unwind the sps mark. - masm.spsUnmarkJit(&cx->runtime()->spsProfiler, rbx); - // Restore non-volatile registers. #if defined(_WIN64) masm.vmovdqa(Operand(rsp, 16 * 0), xmm6); diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index baddb5a1f39c..46f7135a4e26 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -60,9 +60,6 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) masm.push(esi); masm.push(edi); - // Push the EnterJIT sps mark. - masm.spsMarkJit(&cx->runtime()->spsProfiler, ebp, ebx); - // Keep track of the stack which has to be unwound after returning from the // compiled function. masm.movl(esp, esi); @@ -272,21 +269,18 @@ JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) // |ebp| could have been clobbered by the inner function. // Grab the address for the Value result from the argument stack. - // +24 ... arguments ... - // +20 - // +16 ebp <- original %ebp pointing here. - // +12 ebx - // +8 esi - // +4 edi - // +0 hasSPSFrame - masm.loadPtr(Address(esp, ARG_RESULT + 4 * sizeof(void *)), eax); + // +20 ... arguments ... + // +16 + // +12 ebp <- original %ebp pointing here. + // +8 ebx + // +4 esi + // +0 edi + masm.loadPtr(Address(esp, ARG_RESULT + 3 * sizeof(void *)), eax); masm.storeValue(JSReturnOperand, Operand(eax, 0)); /************************************************************** Return stack and registers to correct state **************************************************************/ - // Unwind the sps mark. - masm.spsUnmarkJit(&cx->runtime()->spsProfiler, ebx); // Restore non-volatile registers masm.pop(edi); diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index d7b2f2fb68e8..de970b4dc87c 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -93,7 +93,7 @@ SPSProfiler::enable(bool enabled) * jitcode for scripts with active frames on the stack. These scripts need to have * their profiler state toggled so they behave properly. */ - jit::ToggleBaselineSPS(rt, enabled); + jit::ToggleBaselineProfiling(rt, enabled); /* Update lastProfilingFrame to point to the top-most JS jit-frame currently on * stack. @@ -333,7 +333,6 @@ SPSEntryMarker::SPSEntryMarker(JSRuntime *rt, size_before = *profiler->size_; // We want to push a CPP frame so the profiler can correctly order JS and native stacks. profiler->push("js::RunScript", this, nullptr, nullptr, /* copy = */ false); - // We also want to push a JS frame so the hang monitor can catch script hangs. profiler->push("js::RunScript", nullptr, script, script->code(), /* copy = */ false); } diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h index 9955d1da5012..173037e10182 100644 --- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -287,230 +287,19 @@ class SPSEntryMarker template class SPSInstrumentation { - /* Because of inline frames, this is a nested structure in a vector */ - struct FrameState { - JSScript *script; // script for this frame, nullptr if not pushed yet - jsbytecode *pc; // pc at which this frame was left for entry into a callee - bool skipNext; // should the next call to reenter be skipped? - int left; // number of leave() calls made without a matching reenter() - }; - SPSProfiler *profiler_; // Instrumentation location management - Vector frames; - FrameState *frame; - - static void clearFrame(FrameState *frame) { - frame->script = nullptr; - frame->pc = nullptr; - frame->skipNext = false; - frame->left = 0; - } - public: /* * Creates instrumentation which writes information out the the specified * profiler's stack and constituent fields. */ - explicit SPSInstrumentation(SPSProfiler *profiler) - : profiler_(profiler), frame(nullptr) - { - enterInlineFrame(nullptr); - } + explicit SPSInstrumentation(SPSProfiler *profiler) : profiler_(profiler) {} /* Small proxies around SPSProfiler */ bool enabled() { return profiler_ && profiler_->enabled(); } SPSProfiler *profiler() { MOZ_ASSERT(enabled()); return profiler_; } void disable() { profiler_ = nullptr; } - - /* Signals an inline function returned, reverting to the previous state */ - void leaveInlineFrame() { - if (!enabled()) - return; - MOZ_ASSERT(frame->left == 0); - MOZ_ASSERT(frame->script != nullptr); - frames.shrinkBy(1); - MOZ_ASSERT(frames.length() > 0); - frame = &frames[frames.length() - 1]; - } - - /* Saves the current state and assumes a fresh one for the inline function */ - bool enterInlineFrame(jsbytecode *callerPC) { - if (!enabled()) - return true; - MOZ_ASSERT_IF(frames.empty(), callerPC == nullptr); - - MOZ_ASSERT_IF(frame != nullptr, frame->script != nullptr); - MOZ_ASSERT_IF(frame != nullptr, frame->left == 1); - if (!frames.empty()) { - MOZ_ASSERT(frame == &frames[frames.length() - 1]); - frame->pc = callerPC; - } - if (!frames.growBy(1)) - return false; - frame = &frames[frames.length() - 1]; - clearFrame(frame); - return true; - } - - /* Prepares the instrumenter state for generating OOL code, by - * setting up the frame state to seem as if there are exactly - * two pushed frames: a frame for the top-level script, and - * a frame for the OOL code being generated. Any - * vm-calls from the OOL code will "leave" the OOL frame and - * return back to it. - */ - bool prepareForOOL() { - if (!enabled()) - return true; - MOZ_ASSERT(!frames.empty()); - if (frames.length() >= 2) { - frames.shrinkBy(frames.length() - 2); - - } else { // frames.length() == 1 - if (!frames.growBy(1)) - return false; - } - frames[0].pc = frames[0].script->code(); - frame = &frames[1]; - clearFrame(frame); - return true; - } - void finishOOL() { - if (!enabled()) - return; - MOZ_ASSERT(!frames.empty()); - frames.shrinkBy(frames.length() - 1); - } - - /* Number of inline frames currently active (doesn't include original one) */ - unsigned inliningDepth() { - return frames.length() - 1; - } - - /* - * When debugging or with slow assertions, sometimes a C++ method will be - * invoked to perform the pop operation from the SPS stack. When we leave - * JIT code, we need to record the current PC, but upon reentering JIT - * code, no update back to nullptr should happen. This method exists to - * flag this behavior. The next leave() will emit instrumentation, but the - * following reenter() will be a no-op. - */ - void skipNextReenter() { - /* If we've left the frame, the reenter will be skipped anyway */ - if (!enabled() || frame->left != 0) - return; - MOZ_ASSERT(frame->script); - MOZ_ASSERT(!frame->skipNext); - frame->skipNext = true; - } - - /* - * In some cases, a frame needs to be flagged as having been pushed, but no - * instrumentation should be emitted. This updates internal state to flag - * that further instrumentation should actually be emitted. - */ - void setPushed(JSScript *script) { - if (!enabled()) - return; - MOZ_ASSERT(frame->left == 0); - frame->script = script; - } - - JSScript *getPushed() { - if (!enabled()) - return nullptr; - return frame->script; - } - - /* - * Flags entry into a JS function for the first time. Before this is called, - * no instrumentation is emitted, but after this instrumentation is emitted. - */ - bool push(JSScript *script, Assembler &masm, Register scratch, bool inlinedFunction = false) { - if (!enabled()) - return true; - if (!inlinedFunction) { - const char *string = profiler_->profileString(script, script->functionNonDelazifying()); - if (string == nullptr) - return false; - masm.spsPushFrame(profiler_, string, script, scratch); - } - setPushed(script); - return true; - } - - /* - * Signifies that C++ performed the push() for this function. C++ always - * sets the current PC to something non-null, however, so as soon as JIT - * code is reentered this updates the current pc to nullptr. - */ - void pushManual(JSScript *script, Assembler &masm, Register scratch, - bool inlinedFunction = false) - { - if (!enabled()) - return; - - if (!inlinedFunction) - masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCOffset, scratch); - - setPushed(script); - } - - /* - * Signals that the current function is leaving for a function call. This - * can happen both on JS function calls and also calls to C++. This - * internally manages how many leave() calls have been seen, and only the - * first leave() emits instrumentation. Similarly, only the last - * corresponding reenter() actually emits instrumentation. - */ - void leave(jsbytecode *pc, Assembler &masm, Register scratch, bool inlinedFunction = false) { - if (enabled() && frame->script && frame->left++ == 0) { - jsbytecode *updatePC = pc; - JSScript *script = frame->script; - if (!inlinedFunction) { - // We may be leaving an inlined frame for entry into a C++ frame. - // Use the top script's pc offset instead of the innermost script's. - if (inliningDepth() > 0) { - MOZ_ASSERT(frames[0].pc); - updatePC = frames[0].pc; - script = frames[0].script; - } - } - - if (!inlinedFunction) - masm.spsUpdatePCIdx(profiler_, script->pcToOffset(updatePC), scratch); - } - } - - /* - * Flags that the leaving of the current function has returned. This tracks - * state with leave() to only emit instrumentation at proper times. - */ - void reenter(Assembler &masm, Register scratch, bool inlinedFunction = false) { - if (!enabled() || !frame->script || frame->left-- != 1) - return; - if (frame->skipNext) { - frame->skipNext = false; - } else { - if (!inlinedFunction) - masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCOffset, scratch); - } - } - - /* - * Signifies exiting a JS frame, popping the SPS entry. Because there can be - * multiple return sites of a function, this does not cease instrumentation - * emission. - */ - void pop(Assembler &masm, Register scratch, bool inlinedFunction = false) { - if (enabled()) { - MOZ_ASSERT(frame->left == 0); - MOZ_ASSERT(frame->script); - if (!inlinedFunction) - masm.spsPopFrame(profiler_, scratch); - } - } }; diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 2643115ee1d9..d8c3fd8b3fdc 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1373,7 +1373,8 @@ AbstractFramePtr::hasPushedSPSFrame() const { if (isInterpreterFrame()) return asInterpreterFrame()->hasPushedSPSFrame(); - return asBaselineFrame()->hasPushedSPSFrame(); + MOZ_ASSERT(isBaselineFrame()); + return false; } jit::JitActivation::JitActivation(JSContext *cx, bool active) From aef7e0502eb297d4cf3fe066b297284f32aa40af Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 15 Jan 2015 20:11:22 -0500 Subject: [PATCH 049/133] Bug 1057082 - 6/7 - Modify profiler sampler to use jit stack walking instead of pseudostack. r=jandem r=BenWa --- js/public/ProfilingFrameIterator.h | 46 ++++- js/public/ProfilingStack.h | 41 +++-- js/src/asmjs/AsmJSFrameIterator.cpp | 10 + js/src/asmjs/AsmJSValidate.cpp | 23 ++- js/src/jit/BaselineCompiler.cpp | 25 ++- js/src/jit/CodeGenerator.cpp | 17 +- js/src/jit/Ion.cpp | 9 +- js/src/jit/IonCaches.cpp | 17 +- js/src/jit/IonCode.h | 4 + js/src/jit/JitFrameIterator-inl.h | 13 ++ js/src/jit/JitFrameIterator.h | 29 +++ js/src/jit/JitFrames.cpp | 274 +++++++++++++++++++++++++++- js/src/jit/JitcodeMap.cpp | 255 +++++++++++++++++++++++--- js/src/jit/JitcodeMap.h | 217 ++++++++++++---------- js/src/shell/js.cpp | 6 +- js/src/vm/Interpreter.cpp | 7 +- js/src/vm/Runtime.h | 5 +- js/src/vm/SPSProfiler.cpp | 80 ++++---- js/src/vm/SPSProfiler.h | 25 ++- js/src/vm/Stack.cpp | 178 +++++++++++++++--- js/src/vm/Stack.h | 8 +- tools/profiler/TableTicker.cpp | 173 +++++++++--------- 22 files changed, 1155 insertions(+), 307 deletions(-) diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h index 52bb3a4f3014..4f48034318a4 100644 --- a/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -19,6 +19,10 @@ struct JSRuntime; namespace js { class Activation; class AsmJSProfilingFrameIterator; + namespace jit { + class JitActivation; + class JitProfilingFrameIterator; + } } namespace JS { @@ -29,19 +33,39 @@ namespace JS { // unwound. class JS_PUBLIC_API(ProfilingFrameIterator) { + JSRuntime *rt_; js::Activation *activation_; + // When moving past a JitActivation, we need to save the prevJitTop + // from it to use as the exit-frame pointer when the next caller jit + // activation (if any) comes around. + void *savedPrevJitTop_; + static const unsigned StorageSpace = 6 * sizeof(void*); mozilla::AlignedStorage storage_; js::AsmJSProfilingFrameIterator &asmJSIter() { MOZ_ASSERT(!done()); + MOZ_ASSERT(isAsmJS()); return *reinterpret_cast(storage_.addr()); } const js::AsmJSProfilingFrameIterator &asmJSIter() const { MOZ_ASSERT(!done()); + MOZ_ASSERT(isAsmJS()); return *reinterpret_cast(storage_.addr()); } + js::jit::JitProfilingFrameIterator &jitIter() { + MOZ_ASSERT(!done()); + MOZ_ASSERT(isJit()); + return *reinterpret_cast(storage_.addr()); + } + + const js::jit::JitProfilingFrameIterator &jitIter() const { + MOZ_ASSERT(!done()); + MOZ_ASSERT(isJit()); + return *reinterpret_cast(storage_.addr()); + } + void settle(); public: @@ -65,15 +89,31 @@ class JS_PUBLIC_API(ProfilingFrameIterator) // and less than older native and psuedo-stack frame addresses void *stackAddress() const; - // Return a label suitable for regexp-matching as performed by - // browser/devtools/profiler/cleopatra/js/parserWorker.js - const char *label() const; + enum FrameKind + { + Frame_Baseline, + Frame_Ion, + Frame_AsmJS + }; + + struct Frame + { + FrameKind kind; + void *stackAddress; + void *returnAddress; + void *activation; + const char *label; + }; + uint32_t extractStack(Frame *frames, uint32_t offset, uint32_t end) const; private: void iteratorConstruct(const RegisterState &state); void iteratorConstruct(); void iteratorDestroy(); bool iteratorDone(); + + bool isAsmJS() const; + bool isJit() const; }; } // namespace JS diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h index da4ebaa6eb1f..d66eb264f66c 100644 --- a/js/public/ProfilingStack.h +++ b/js/public/ProfilingStack.h @@ -60,24 +60,29 @@ class ProfileEntry // sample of the pseudostack. FRAME_LABEL_COPY = 0x02, - // This ProfileEntry was pushed immediately before calling into asm.js. - ASMJS = 0x04, + // This ProfileEntry is a dummy entry indicating the start of a run + // of JS pseudostack entries. + BEGIN_PSEUDO_JS = 0x04, + + // This flag is used to indicate that an interpreter JS entry has OSR-ed + // into baseline. + OSR = 0x08, // Mask for removing all flags except the category information. - CATEGORY_MASK = ~IS_CPP_ENTRY & ~FRAME_LABEL_COPY & ~ASMJS + CATEGORY_MASK = ~IS_CPP_ENTRY & ~FRAME_LABEL_COPY & ~BEGIN_PSEUDO_JS & ~OSR }; // Keep these in sync with browser/devtools/profiler/utils/global.js MOZ_BEGIN_NESTED_ENUM_CLASS(Category, uint32_t) - OTHER = 0x08, - CSS = 0x10, - JS = 0x20, - GC = 0x40, - CC = 0x80, - NETWORK = 0x100, - GRAPHICS = 0x200, - STORAGE = 0x400, - EVENTS = 0x800, + OTHER = 0x10, + CSS = 0x20, + JS = 0x40, + GC = 0x80, + CC = 0x100, + NETWORK = 0x200, + GRAPHICS = 0x400, + STORAGE = 0x800, + EVENTS = 0x1000, FIRST = OTHER, LAST = EVENTS @@ -126,6 +131,18 @@ class ProfileEntry return flags_ & CATEGORY_MASK; } + void setOSR() volatile { + MOZ_ASSERT(isJs()); + setFlag(OSR); + } + void unsetOSR() volatile { + MOZ_ASSERT(isJs()); + unsetFlag(OSR); + } + bool isOSR() const volatile { + return hasFlag(OSR); + } + void *stackAddress() const volatile { MOZ_ASSERT(!isJs()); return spOrScript; diff --git a/js/src/asmjs/AsmJSFrameIterator.cpp b/js/src/asmjs/AsmJSFrameIterator.cpp index df435d6676b8..8fae349df030 100644 --- a/js/src/asmjs/AsmJSFrameIterator.cpp +++ b/js/src/asmjs/AsmJSFrameIterator.cpp @@ -414,6 +414,16 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation & exitReason_(AsmJSExit::None), codeRange_(nullptr) { + // If profiling hasn't been enabled for this module, then CallerFPFromFP + // will be trash, so ignore the entire activation. In practice, this only + // happens if profiling is enabled while module->active() (in this case, + // profiling will be enabled when the module becomes inactive and gets + // called again). + if (!module_->profilingEnabled()) { + MOZ_ASSERT(done()); + return; + } + initFromFP(activation); } diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 12833a2c9574..f61e89c68837 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -8511,6 +8511,8 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit // cx->mainThread().jitJSContext = cx; // act.prevJitActivation_ = cx->mainThread().jitActivation; // cx->mainThread().jitActivation = act; + // act.prevProfilingActivation_ = cx->mainThread().profilingActivation; + // cx->mainThread().profilingActivation_ = act; // On the ARM store8() uses the secondScratchReg (lr) as a temp. size_t offsetOfActivation = offsetof(JSRuntime, mainThread) + PerThreadData::offsetOfActivation(); @@ -8519,6 +8521,8 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit offsetof(PerThreadData, jitJSContext); size_t offsetOfJitActivation = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitActivation); + size_t offsetOfProfilingActivation = offsetof(JSRuntime, mainThread) + + PerThreadData::offsetOfProfilingActivation(); masm.loadAsmJSActivation(reg0); masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3); masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0); @@ -8542,6 +8546,12 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitActivation())); // cx->mainThread().jitActivation = act; masm.storePtr(reg1, Address(reg0, offsetOfJitActivation)); + + // act.prevProfilingActivation_ = cx->mainThread().profilingActivation; + masm.loadPtr(Address(reg0, offsetOfProfilingActivation), reg2); + masm.storePtr(reg2, Address(reg1, Activation::offsetOfPrevProfiling())); + // cx->mainThread().profilingActivation_ = act; + masm.storePtr(reg1, Address(reg0, offsetOfProfilingActivation)); } // 2. Call @@ -8561,6 +8571,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit Register reg2 = AsmJSIonExitRegD2; // The following is inlined: + // rt->mainThread.profilingActivation = prevProfilingActivation_; // rt->mainThread.activation()->active_ = false; // rt->mainThread.jitTop = prevJitTop_; // rt->mainThread.jitJSContext = prevJitJSContext_; @@ -8573,17 +8584,23 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit offsetof(PerThreadData, jitJSContext); size_t offsetOfJitActivation = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitActivation); + size_t offsetOfProfilingActivation = offsetof(JSRuntime, mainThread) + + PerThreadData::offsetOfProfilingActivation(); masm.movePtr(AsmJSImmPtr(AsmJSImm_Runtime), reg0); masm.loadPtr(Address(reg0, offsetOfActivation), reg1); - // rt->mainThread.activation()->active_ = false; - masm.store8(Imm32(0), Address(reg1, JitActivation::offsetOfActiveUint8())); - // rt->mainThread.jitTop = prevJitTop_; masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitTop()), reg2); masm.storePtr(reg2, Address(reg0, offsetOfJitTop)); + // rt->mainThread.profilingActivation = rt->mainThread.activation()->prevProfiling_; + masm.loadPtr(Address(reg1, Activation::offsetOfPrevProfiling()), reg2); + masm.storePtr(reg2, Address(reg0, offsetOfProfilingActivation)); + + // rt->mainThread.activation()->active_ = false; + masm.store8(Imm32(0), Address(reg1, JitActivation::offsetOfActiveUint8())); + // rt->mainThread.jitJSContext = prevJitJSContext_; masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitJSContext()), reg2); masm.storePtr(reg2, Address(reg0, offsetOfJitJSContext)); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 154b871239e8..01edc4b48b56 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -261,23 +261,32 @@ BaselineCompiler::compile() if (compileDebugInstrumentation_) baselineScript->setHasDebugInstrumentation(); - // If profiler instrumentation is enabled, register a native => bytecode mapping entry, - // and toggle profiling on - if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) { + // If profiler instrumentation is enabled, toggle instrumentation on. + if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) + baselineScript->toggleProfilerInstrumentation(true); + + // Always register a native => bytecode mapping entry, since profiler can be + // turned on with baseline jitcode on stack, and baseline jitcode cannot be invalidated. + { JitSpew(JitSpew_Profiling, "Added JitcodeGlobalEntry for baseline script %s:%d (%p)", script->filename(), script->lineno(), baselineScript.get()); + + // Generate profiling string. + char *str = JitcodeGlobalEntry::createScriptString(cx, script); + if (!str) + return Method_Error; + JitcodeGlobalEntry::BaselineEntry entry; - entry.init(code->raw(), code->raw() + code->instructionsSize(), script); + entry.init(code->raw(), code->rawEnd(), script, str); JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); - if (!globalTable->addEntry(entry)) + if (!globalTable->addEntry(entry, cx->runtime())) { + entry.destroy(); return Method_Error; + } // Mark the jitcode as having a bytecode map. code->setHasBytecodeMap(); - - // Toggle profiler instrumentation on in the jitcode. - baselineScript->toggleProfilerInstrumentation(true); } script->setBaselineScript(cx, baselineScript.release()); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 60c82a40f05c..61ddde8f3d08 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -7180,7 +7180,22 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) // Add entry to the global table. JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); - if (!globalTable->addEntry(entry)) { + if (!globalTable->addEntry(entry, cx->runtime())) { + // Memory may have been allocated for the entry. + entry.destroy(); + return false; + } + + // Mark the jitcode as having a bytecode map. + code->setHasBytecodeMap(); + } else { + // Add a dumy jitcodeGlobalTable entry. + JitcodeGlobalEntry::DummyEntry entry; + entry.init(code->raw(), code->rawEnd()); + + // Add entry to the global table. + JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); + if (!globalTable->addEntry(entry, cx->runtime())) { // Memory may have been allocated for the entry. entry.destroy(); return false; diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 1c2916aecdb5..d0ce623ec8ca 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -656,17 +656,18 @@ JitCode::trace(JSTracer *trc) void JitCode::finalize(FreeOp *fop) { + JSRuntime *rt = fop->runtime(); + // If this jitcode has a bytecode map, de-register it. if (hasBytecodeMap_) { - MOZ_ASSERT(fop->runtime()->jitRuntime()->hasJitcodeGlobalTable()); - fop->runtime()->jitRuntime()->getJitcodeGlobalTable()->removeEntry(raw()); + MOZ_ASSERT(rt->jitRuntime()->hasJitcodeGlobalTable()); + rt->jitRuntime()->getJitcodeGlobalTable()->removeEntry(raw(), rt); } // Buffer can be freed at any time hereafter. Catch use-after-free bugs. // Don't do this if the Ion code is protected, as the signal handler will // deadlock trying to reacquire the interrupt lock. - if (fop->runtime()->jitRuntime()) - memset(code_, JS_SWEPT_CODE_PATTERN, bufferSize_); + memset(code_, JS_SWEPT_CODE_PATTERN, bufferSize_); code_ = nullptr; // Code buffers are stored inside JSC pools. diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 13799c92aaef..32505e3463e9 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -431,11 +431,24 @@ IonCache::linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &a // Add entry to native => bytecode mapping for this stub if needed. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) { JitcodeGlobalEntry::IonCacheEntry entry; - entry.init(code->raw(), code->raw() + code->instructionsSize(), rejoinAddress()); + entry.init(code->raw(), code->rawEnd(), rejoinAddress()); // Add entry to the global table. JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); - if (!globalTable->addEntry(entry)) { + if (!globalTable->addEntry(entry, cx->runtime())) { + entry.destroy(); + return false; + } + + // Mark the jitcode as having a bytecode map. + code->setHasBytecodeMap(); + } else { + JitcodeGlobalEntry::DummyEntry entry; + entry.init(code->raw(), code->rawEnd()); + + // Add entry to the global table. + JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); + if (!globalTable->addEntry(entry, cx->runtime())) { entry.destroy(); return false; } diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index 3791b071203f..848281dfc49c 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -98,6 +98,10 @@ class JitCode : public gc::TenuredCell uint8_t *rawEnd() const { return code_ + insnSize_; } + bool containsNativePC(const void *addr) const { + const uint8_t *addr_u8 = (const uint8_t *) addr; + return raw() <= addr_u8 && addr_u8 < rawEnd(); + } size_t instructionsSize() const { return insnSize_; } diff --git a/js/src/jit/JitFrameIterator-inl.h b/js/src/jit/JitFrameIterator-inl.h index ad619777ebc3..f6f29b71cd52 100644 --- a/js/src/jit/JitFrameIterator-inl.h +++ b/js/src/jit/JitFrameIterator-inl.h @@ -16,6 +16,19 @@ namespace js { namespace jit { +inline JitFrameLayout * +JitProfilingFrameIterator::framePtr() +{ + MOZ_ASSERT(!done()); + return (JitFrameLayout *) fp_; +} + +inline JSScript * +JitProfilingFrameIterator::frameScript() +{ + return ScriptFromCalleeToken(framePtr()->calleeToken()); +} + inline BaselineFrame * JitFrameIterator::baselineFrame() const { diff --git a/js/src/jit/JitFrameIterator.h b/js/src/jit/JitFrameIterator.h index 4e053b88d829..80c92caf3c3d 100644 --- a/js/src/jit/JitFrameIterator.h +++ b/js/src/jit/JitFrameIterator.h @@ -14,6 +14,8 @@ #include "jit/IonCode.h" #include "jit/Snapshots.h" +#include "js/ProfilingFrameIterator.h" + namespace js { class ActivationIterator; }; @@ -255,6 +257,33 @@ class JitFrameIterator #endif }; +class JitcodeGlobalTable; + +class JitProfilingFrameIterator +{ + uint8_t *fp_; + FrameType type_; + void *returnAddressToFp_; + + inline JitFrameLayout *framePtr(); + inline JSScript *frameScript(); + bool tryInitWithPC(void *pc); + bool tryInitWithTable(JitcodeGlobalTable *table, void *pc, JSRuntime *rt); + + public: + JitProfilingFrameIterator(JSRuntime *rt, + const JS::ProfilingFrameIterator::RegisterState &state); + explicit JitProfilingFrameIterator(void *exitFrame); + + void operator++(); + bool done() const { return fp_ == nullptr; } + + void *fp() const { MOZ_ASSERT(!done()); return fp_; } + void *stackAddress() const { return fp(); } + FrameType frameType() const { MOZ_ASSERT(!done()); return type_; } + void *returnAddressToFp() const { MOZ_ASSERT(!done()); return returnAddressToFp_; } +}; + class RInstructionResults { // Vector of results of recover instructions. diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 15a847be70f7..d57632165e1c 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -2671,7 +2671,7 @@ JitFrameIterator::verifyReturnAddressUsingNativeToBytecodeMap() // Look up and print bytecode info for the native address. JitcodeGlobalEntry entry; - if (!jitrt->getJitcodeGlobalTable()->lookup(returnAddressToFp_, &entry)) + if (!jitrt->getJitcodeGlobalTable()->lookup(returnAddressToFp_, &entry, rt)) return true; JitSpew(JitSpew_Profiling, "Found nativeToBytecode entry for %p: %p - %p", @@ -2718,6 +2718,278 @@ JitFrameIterator::verifyReturnAddressUsingNativeToBytecodeMap() } #endif // DEBUG +JitProfilingFrameIterator::JitProfilingFrameIterator( + JSRuntime *rt, const JS::ProfilingFrameIterator::RegisterState &state) +{ + // If no profilingActivation is live, initialize directly to + // end-of-iteration state. + if (!rt->mainThread.profilingActivation()) { + type_ = JitFrame_Entry; + fp_ = nullptr; + returnAddressToFp_ = nullptr; + return; + } + + MOZ_ASSERT(rt->mainThread.profilingActivation()->isJit()); + + JitActivation *act = rt->mainThread.profilingActivation()->asJit(); + + // If the top JitActivation has a null lastProfilingFrame, assume that + // it's a trivially empty activation, and initialize directly + // to end-of-iteration state. + if (!act->lastProfilingFrame()) { + type_ = JitFrame_Entry; + fp_ = nullptr; + returnAddressToFp_ = nullptr; + return; + } + + // Get the fp from the current profilingActivation + fp_ = (uint8_t *) act->lastProfilingFrame(); + void *lastCallSite = act->lastProfilingCallSite(); + + JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable(); + + // Profiler sampling must NOT be suppressed if we are here. + MOZ_ASSERT(rt->isProfilerSamplingEnabled()); + + // Since the frame is on stack, and is a jit frame, it MUST have Baseline jitcode. + MOZ_ASSERT(frameScript()->hasBaselineScript()); + + // Try initializing with sampler pc + if (tryInitWithPC(state.pc)) + return; + + // Try initializing with sampler pc using native=>bytecode table. + if (tryInitWithTable(table, state.pc, rt)) + return; + + // Try initializing with lastProfilingCallSite pc + if (lastCallSite) { + if (tryInitWithPC(lastCallSite)) + return; + + // Try initializing with lastProfilingCallSite pc using native=>bytecode table. + if (tryInitWithTable(table, lastCallSite, rt)) + return; + } + + // If nothing matches, for now just assume we are at the start of the last frame's + // baseline jit code. + type_ = JitFrame_BaselineJS; + returnAddressToFp_ = frameScript()->baselineScript()->method()->raw(); + //++(*this); +} + +template +inline ReturnType +GetPreviousRawFrame(FrameType *frame) +{ + size_t prevSize = frame->prevFrameLocalSize() + FrameType::Size(); + return (ReturnType) (((uint8_t *) frame) + prevSize); +} + +JitProfilingFrameIterator::JitProfilingFrameIterator(void *exitFrame) +{ + // Exit frame was en + ExitFrameLayout *frame = (ExitFrameLayout *) exitFrame; + FrameType prevType = frame->prevType(); + + if (prevType == JitFrame_IonJS || prevType == JitFrame_BaselineJS || + prevType == JitFrame_Unwound_IonJS) + { + returnAddressToFp_ = frame->returnAddress(); + fp_ = GetPreviousRawFrame(frame); + type_ = JitFrame_IonJS; + return; + } + + if (prevType == JitFrame_BaselineStub || prevType == JitFrame_Unwound_BaselineStub) { + BaselineStubFrameLayout *stubFrame = + GetPreviousRawFrame(frame); + MOZ_ASSERT_IF(prevType == JitFrame_BaselineStub, + stubFrame->prevType() == JitFrame_BaselineJS); + MOZ_ASSERT_IF(prevType == JitFrame_Unwound_BaselineStub, + stubFrame->prevType() == JitFrame_BaselineJS || + stubFrame->prevType() == JitFrame_IonJS); + returnAddressToFp_ = stubFrame->returnAddress(); + fp_ = ((uint8_t *) stubFrame->reverseSavedFramePtr()) + + jit::BaselineFrame::FramePointerOffset; + type_ = JitFrame_BaselineJS; + return; + } + + MOZ_CRASH("Invalid frame type prior to exit frame."); +} + +bool +JitProfilingFrameIterator::tryInitWithPC(void *pc) +{ + JSScript *callee = frameScript(); + + // Check for Ion first, since it's more likely for hot code. + if (callee->hasIonScript() && callee->ionScript()->method()->containsNativePC(pc)) { + type_ = JitFrame_IonJS; + returnAddressToFp_ = pc; + return true; + } + + // Check for containment in Baseline jitcode second. + if (callee->baselineScript()->method()->containsNativePC(pc)) { + type_ = JitFrame_BaselineJS; + returnAddressToFp_ = pc; + return true; + } + + return false; +} + +bool +JitProfilingFrameIterator::tryInitWithTable(JitcodeGlobalTable *table, void *pc, JSRuntime *rt) +{ + if (!pc) + return false; + + JitcodeGlobalEntry entry; + if (!table->lookup(pc, &entry, rt)) + return false; + + JSScript *callee = frameScript(); + + MOZ_ASSERT(entry.isIon() || entry.isBaseline() || entry.isIonCache()); + if (entry.isIon()) { + // If looked-up callee doesn't match frame callee, don't accept lastProfilingCallSite + if (entry.ionEntry().getScript(0) != callee) + return false; + + type_ = JitFrame_IonJS; + returnAddressToFp_ = pc; + return true; + } + + if (entry.isBaseline()) { + // If looked-up callee doesn't match frame callee, don't accept lastProfilingCallSite + if (entry.baselineEntry().script() != callee) + return false; + + type_ = JitFrame_BaselineJS; + returnAddressToFp_ = pc; + return true; + } + + if (entry.isIonCache()) { + JitcodeGlobalEntry ionEntry; + table->lookupInfallible(entry.ionCacheEntry().rejoinAddr(), &ionEntry, rt); + MOZ_ASSERT(ionEntry.isIon()); + + if (ionEntry.ionEntry().getScript(0) != callee) + return false; + + type_ = JitFrame_IonJS; + returnAddressToFp_ = entry.ionCacheEntry().rejoinAddr(); + return true; + } + + return false; +} + +void +JitProfilingFrameIterator::operator++() +{ + /* + * fp_ points to a Baseline or Ion frame. The possible call-stacks + * patterns occurring between this frame and a previous Ion or Baseline + * frame are as follows: + * + * + * ^ + * | + * ^--- Ion + * | + * ^--- Baseline Stub <---- Baseline + * | + * ^--- Argument Rectifier + * | ^ + * | | + * | ^--- Ion + * | | + * | ^--- Baseline Stub <---- Baseline + * | + * ^--- Entry Frame (From C++) + * Exit Frame (From previous JitActivation) + * ^ + * | + * ^--- Ion + * | + * ^--- Baseline + * | + * ^--- Baseline Stub <---- Baseline + */ + JitFrameLayout *frame = framePtr(); + FrameType prevType = frame->prevType(); + + if (prevType == JitFrame_IonJS) { + returnAddressToFp_ = frame->returnAddress(); + fp_ = GetPreviousRawFrame(frame); + type_ = JitFrame_IonJS; + return; + } + + if (prevType == JitFrame_BaselineJS) { + returnAddressToFp_ = frame->returnAddress(); + fp_ = GetPreviousRawFrame(frame); + type_ = JitFrame_BaselineJS; + return; + } + + if (prevType == JitFrame_BaselineStub) { + BaselineStubFrameLayout *stubFrame = + GetPreviousRawFrame(frame); + MOZ_ASSERT(stubFrame->prevType() == JitFrame_BaselineJS); + + returnAddressToFp_ = stubFrame->returnAddress(); + fp_ = ((uint8_t *) stubFrame->reverseSavedFramePtr()) + + jit::BaselineFrame::FramePointerOffset; + type_ = JitFrame_BaselineJS; + return; + } + + if (prevType == JitFrame_Rectifier) { + RectifierFrameLayout *rectFrame = + GetPreviousRawFrame(frame); + FrameType rectPrevType = rectFrame->prevType(); + + if (rectPrevType == JitFrame_IonJS) { + returnAddressToFp_ = rectFrame->returnAddress(); + fp_ = GetPreviousRawFrame(rectFrame); + type_ = JitFrame_IonJS; + return; + } + + if (rectPrevType == JitFrame_BaselineStub) { + BaselineStubFrameLayout *stubFrame = + GetPreviousRawFrame(rectFrame); + returnAddressToFp_ = stubFrame->returnAddress(); + fp_ = ((uint8_t *) stubFrame->reverseSavedFramePtr()) + + jit::BaselineFrame::FramePointerOffset; + type_ = JitFrame_BaselineJS; + return; + } + + MOZ_CRASH("Bad frame type prior to rectifier frame."); + } + + if (prevType == JitFrame_Entry) { + // No previous frame, set to null to indicate that JitFrameIterator is done() + returnAddressToFp_ = nullptr; + fp_ = nullptr; + type_ = JitFrame_Entry; + return; + } + + MOZ_CRASH("Bad frame type."); +} + JitFrameLayout * InvalidationBailoutStack::fp() const { diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index 5a5e0494d4c3..01655c149c45 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -7,10 +7,15 @@ #include "jit/JitcodeMap.h" #include "mozilla/DebugOnly.h" +#include "mozilla/UniquePtr.h" +#include "jsprf.h" + #include "jit/BaselineJIT.h" #include "jit/JitSpewer.h" #include "js/Vector.h" +#include "vm/SPSProfiler.h" +#include "jsscriptinlines.h" namespace js { namespace jit { @@ -51,6 +56,38 @@ JitcodeGlobalEntry::IonEntry::callStackAtAddr(JSRuntime *rt, void *ptr, return true; } +uint32_t +JitcodeGlobalEntry::IonEntry::callStackAtAddr(JSRuntime *rt, void *ptr, + const char **results, + uint32_t maxResults) const +{ + MOZ_ASSERT(containsPointer(ptr)); + MOZ_ASSERT(maxResults >= 1); + uint32_t ptrOffset = reinterpret_cast(ptr) - + reinterpret_cast(nativeStartAddr()); + + uint32_t regionIdx = regionTable()->findRegionEntry(ptrOffset); + MOZ_ASSERT(regionIdx < regionTable()->numRegions()); + + JitcodeRegionEntry region = regionTable()->regionEntry(regionIdx); + + JitcodeRegionEntry::ScriptPcIterator locationIter = region.scriptPcIterator(); + MOZ_ASSERT(locationIter.hasMore()); + uint32_t count = 0; + while (locationIter.hasMore()) { + uint32_t scriptIdx, pcOffset; + + locationIter.readNext(&scriptIdx, &pcOffset); + MOZ_ASSERT(getStr(scriptIdx)); + + results[count++] = getStr(scriptIdx); + if (count >= maxResults) + break; + } + + return count; +} + void JitcodeGlobalEntry::IonEntry::destroy() { @@ -63,11 +100,15 @@ JitcodeGlobalEntry::IonEntry::destroy() js_free((void*) (regionTable_->payloadStart())); regionTable_ = nullptr; - // Single tag is just pointer-to-jsscript, no memory to free. - ScriptListTag tag = scriptListTag(); - if (tag > Single) - js_free(scriptListPointer()); - scriptList_ = 0; + // Free the scriptList strs. + for (uint32_t i = 0; i < scriptList_->size; i++) { + js_free(scriptList_->pairs[i].str); + scriptList_->pairs[i].str = nullptr; + } + + // Free the script list + js_free(scriptList_); + scriptList_ = nullptr; } bool @@ -88,6 +129,28 @@ JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(JSRuntime *rt, void *ptr, return true; } +uint32_t +JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(JSRuntime *rt, void *ptr, + const char **results, + uint32_t maxResults) const +{ + MOZ_ASSERT(containsPointer(ptr)); + MOZ_ASSERT(script_->hasBaselineScript()); + MOZ_ASSERT(maxResults >= 1); + + results[0] = str(); + return 1; +} + +void +JitcodeGlobalEntry::BaselineEntry::destroy() +{ + if (!str_) + return; + js_free(str_); + str_ = nullptr; +} + bool JitcodeGlobalEntry::IonCacheEntry::callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results, @@ -98,12 +161,28 @@ JitcodeGlobalEntry::IonCacheEntry::callStackAtAddr(JSRuntime *rt, void *ptr, // There must exist an entry for the rejoin addr if this entry exists. JitRuntime *jitrt = rt->jitRuntime(); JitcodeGlobalEntry entry; - jitrt->getJitcodeGlobalTable()->lookupInfallible(rejoinAddr(), &entry); + jitrt->getJitcodeGlobalTable()->lookupInfallible(rejoinAddr(), &entry, rt); MOZ_ASSERT(entry.isIon()); return entry.callStackAtAddr(rt, rejoinAddr(), results, depth); } +uint32_t +JitcodeGlobalEntry::IonCacheEntry::callStackAtAddr(JSRuntime *rt, void *ptr, + const char **results, + uint32_t maxResults) const +{ + MOZ_ASSERT(containsPointer(ptr)); + + // There must exist an entry for the rejoin addr if this entry exists. + JitRuntime *jitrt = rt->jitRuntime(); + JitcodeGlobalEntry entry; + jitrt->getJitcodeGlobalTable()->lookupInfallible(rejoinAddr(), &entry, rt); + MOZ_ASSERT(entry.isIon()); + + return entry.callStackAtAddr(rt, rejoinAddr(), results, maxResults); +} + static int ComparePointers(const void *a, const void *b) { const uint8_t *a_ptr = reinterpret_cast(a); @@ -144,34 +223,135 @@ JitcodeGlobalEntry::compare(const JitcodeGlobalEntry &ent1, const JitcodeGlobalE return flip * -1; } +/* static */ char * +JitcodeGlobalEntry::createScriptString(JSContext *cx, JSScript *script, size_t *length) +{ + // If the script has a function, try calculating its name. + bool hasName = false; + size_t nameLength = 0; + mozilla::UniquePtr nameStr = nullptr; + JSFunction *func = script->functionDelazifying(); + if (func && func->displayAtom()) { + JSAtom *atom = func->displayAtom(); + + JS::AutoCheckCannotGC nogc; + nameStr = mozilla::UniquePtr( + atom->hasLatin1Chars() ? + JS::CharsToNewUTF8CharsZ(cx, atom->latin1Range(nogc)).c_str() + : JS::CharsToNewUTF8CharsZ(cx, atom->twoByteRange(nogc)).c_str()); + if (!nameStr) + return nullptr; + + nameLength = strlen(nameStr.get()); + hasName = true; + } + + // Calculate filename length + const char *filenameStr = script->filename() ? script->filename() : "(null)"; + size_t filenameLength = strlen(filenameStr); + + // Calculate lineno length + bool hasLineno = false; + size_t linenoLength = 0; + char linenoStr[15]; + if (hasName || (script->functionNonDelazifying() || script->isForEval())) { + linenoLength = JS_snprintf(linenoStr, 15, "%u", (unsigned) script->lineno()); + hasLineno = true; + } + + // Full profile string for scripts with functions is: + // FuncName (FileName:Lineno) + // Full profile string for scripts without functions is: + // FileName:Lineno + // Full profile string for scripts without functions and without linenos is: + // FileName + + // Calculate full string length. + size_t fullLength = 0; + if (hasName) { + MOZ_ASSERT(hasLineno); + fullLength = nameLength + 2 + filenameLength + 1 + linenoLength + 1; + } else if (hasLineno) { + fullLength = filenameLength + 1 + linenoLength; + } else { + fullLength = filenameLength; + } + + // Allocate string. + char *str = cx->pod_malloc(fullLength + 1); + if (!str) + return nullptr; + + size_t cur = 0; + + // Fill string with func name if needed. + if (hasName) { + memcpy(str + cur, nameStr.get(), nameLength); + cur += nameLength; + str[cur++] = ' '; + str[cur++] = '('; + } + + // Fill string with filename chars. + memcpy(str + cur, filenameStr, filenameLength); + cur += filenameLength; + + // Fill lineno chars. + if (hasLineno) { + str[cur++] = ':'; + memcpy(str + cur, linenoStr, linenoLength); + cur += linenoLength; + } + + // Terminal ')' if necessary. + if (hasName) + str[cur++] = ')'; + + MOZ_ASSERT(cur == fullLength); + str[cur] = 0; + + if (length) + *length = fullLength; + + return str; +} + bool -JitcodeGlobalTable::lookup(void *ptr, JitcodeGlobalEntry *result) +JitcodeGlobalTable::lookup(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt) { MOZ_ASSERT(result); // Construct a JitcodeGlobalEntry::Query to do the lookup JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(ptr); + + // Lookups on tree does mutation. Suppress sampling when this is happening. + AutoSuppressProfilerSampling suppressSampling(rt); return tree_.contains(query, result); } void -JitcodeGlobalTable::lookupInfallible(void *ptr, JitcodeGlobalEntry *result) +JitcodeGlobalTable::lookupInfallible(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt) { - mozilla::DebugOnly success = lookup(ptr, result); + mozilla::DebugOnly success = lookup(ptr, result, rt); MOZ_ASSERT(success); } bool -JitcodeGlobalTable::addEntry(const JitcodeGlobalEntry &entry) +JitcodeGlobalTable::addEntry(const JitcodeGlobalEntry &entry, JSRuntime *rt) { - // Should only add Main entries for now. - MOZ_ASSERT(entry.isIon() || entry.isBaseline() || entry.isIonCache()); + // Suppress profiler sampling while table is being mutated. + AutoSuppressProfilerSampling suppressSampling(rt); + + MOZ_ASSERT(entry.isIon() || entry.isBaseline() || entry.isIonCache() || entry.isDummy()); return tree_.insert(entry); } void -JitcodeGlobalTable::removeEntry(void *startAddr) +JitcodeGlobalTable::removeEntry(void *startAddr, JSRuntime *rt) { + // Suppress profiler sampling while table is being mutated. + AutoSuppressProfilerSampling suppressSampling(rt); + JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(startAddr); JitcodeGlobalEntry result; mozilla::DebugOnly success = tree_.contains(query, &result); @@ -556,6 +736,26 @@ JitcodeRegionEntry::findPcOffset(uint32_t queryNativeOffset, uint32_t startPcOff return curPcOffset; } +typedef js::Vector ProfilingStringVector; + +struct AutoFreeProfilingStrings { + ProfilingStringVector &profilingStrings_; + bool keep_; + explicit AutoFreeProfilingStrings(ProfilingStringVector &vec) + : profilingStrings_(vec), + keep_(false) + {} + + void keepStrings() { keep_ = true; } + + ~AutoFreeProfilingStrings() { + if (keep_) + return; + for (size_t i = 0; i < profilingStrings_.length(); i++) + js_free(profilingStrings_[i]); + } +}; + bool JitcodeIonTable::makeIonEntry(JSContext *cx, JitCode *code, uint32_t numScripts, JSScript **scripts, @@ -565,25 +765,32 @@ JitcodeIonTable::makeIonEntry(JSContext *cx, JitCode *code, MOZ_ASSERT(numScripts > 0); - if (numScripts == 1) { - out.init(code->raw(), code->rawEnd(), scripts[0], this); - return true; - } + // Create profiling strings for script, within vector. + typedef js::Vector ProfilingStringVector; - if (numScripts < uint32_t(JitcodeGlobalEntry::IonEntry::Multi)) { - JSScript **scriptsCopy = cx->pod_malloc(numScripts); - if (!scriptsCopy) + ProfilingStringVector profilingStrings; + if (!profilingStrings.reserve(numScripts)) + return false; + + AutoFreeProfilingStrings autoFreeProfilingStrings(profilingStrings); + for (uint32_t i = 0; i < numScripts; i++) { + char *str = JitcodeGlobalEntry::createScriptString(cx, scripts[i]); + if (!str) + return false; + if (!profilingStrings.append(str)) return false; - memcpy(scriptsCopy, scripts, sizeof(JSScript *) * numScripts); - out.init(code->raw(), code->rawEnd(), numScripts, scriptsCopy, this); - return true; } // Create SizedScriptList void *mem = (void *)cx->pod_malloc(SizedScriptList::AllocSizeFor(numScripts)); if (!mem) return false; - SizedScriptList *scriptList = new (mem) SizedScriptList(numScripts, scripts); + + // Keep allocated profiling strings on destruct. + autoFreeProfilingStrings.keepStrings(); + + SizedScriptList *scriptList = new (mem) SizedScriptList(numScripts, scripts, + &profilingStrings[0]); out.init(code->raw(), code->rawEnd(), scriptList, this); return true; } diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h index b613dd9fc763..df81271afc50 100644 --- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -41,6 +41,7 @@ class JitcodeGlobalEntry Ion, Baseline, IonCache, + Dummy, Query, LIMIT }; @@ -52,6 +53,7 @@ class JitcodeGlobalEntry BytecodeLocation(JSScript *script, jsbytecode *pc) : script(script), pc(pc) {} }; typedef Vector BytecodeLocationVector; + typedef Vector ProfileStringVector; struct BaseEntry { @@ -97,8 +99,6 @@ class JitcodeGlobalEntry struct IonEntry : public BaseEntry { - uintptr_t scriptList_; - // regionTable_ points to the start of the region table within the // packed map for compile represented by this entry. Since the // region table occurs at the tail of the memory region, this pointer @@ -106,111 +106,54 @@ class JitcodeGlobalEntry // of the memory space. JitcodeIonTable *regionTable_; - static const unsigned LowBits = 3; - static const uintptr_t LowMask = (uintptr_t(1) << LowBits) - 1; - - enum ScriptListTag { - Single = 0, - Multi = 7 + struct ScriptNamePair { + JSScript *script; + char *str; }; struct SizedScriptList { uint32_t size; - JSScript *scripts[0]; - SizedScriptList(uint32_t sz, JSScript **scr) : size(sz) { - for (uint32_t i = 0; i < size; i++) - scripts[i] = scr[i]; + ScriptNamePair pairs[0]; + SizedScriptList(uint32_t sz, JSScript **scrs, char **strs) : size(sz) { + for (uint32_t i = 0; i < size; i++) { + pairs[i].script = scrs[i]; + pairs[i].str = strs[i]; + } } static uint32_t AllocSizeFor(uint32_t nscripts) { - return sizeof(SizedScriptList) + (nscripts * sizeof(JSScript *)); + return sizeof(SizedScriptList) + (nscripts * sizeof(ScriptNamePair)); } }; - void init(void *nativeStartAddr, void *nativeEndAddr, - JSScript *script, JitcodeIonTable *regionTable) - { - MOZ_ASSERT((uintptr_t(script) & LowMask) == 0); - MOZ_ASSERT(script); - MOZ_ASSERT(regionTable); - BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr); - scriptList_ = uintptr_t(script); - regionTable_ = regionTable; - } + SizedScriptList *scriptList_; void init(void *nativeStartAddr, void *nativeEndAddr, - unsigned numScripts, JSScript **scripts, JitcodeIonTable *regionTable) + SizedScriptList *scriptList, JitcodeIonTable *regionTable) { - MOZ_ASSERT((uintptr_t(scripts) & LowMask) == 0); - MOZ_ASSERT(numScripts >= 1); - MOZ_ASSERT(numScripts <= 6); - MOZ_ASSERT(scripts); + MOZ_ASSERT(scriptList); MOZ_ASSERT(regionTable); BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr); - scriptList_ = uintptr_t(scripts) | numScripts; regionTable_ = regionTable; + scriptList_ = scriptList; } - void init(void *nativeStartAddr, void *nativeEndAddr, - SizedScriptList *scripts, JitcodeIonTable *regionTable) - { - MOZ_ASSERT((uintptr_t(scripts) & LowMask) == 0); - MOZ_ASSERT(scripts->size > 6); - MOZ_ASSERT(scripts); - MOZ_ASSERT(regionTable); - - BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr); - scriptList_ = uintptr_t(scripts) | uintptr_t(Multi); - regionTable_ = regionTable; - } - - ScriptListTag scriptListTag() const { - return static_cast(scriptList_ & LowMask); - } - void *scriptListPointer() const { - return reinterpret_cast(scriptList_ & ~LowMask); - } - - JSScript *singleScript() const { - MOZ_ASSERT(scriptListTag() == Single); - return reinterpret_cast(scriptListPointer()); - } - JSScript **rawScriptArray() const { - MOZ_ASSERT(scriptListTag() < Multi); - return reinterpret_cast(scriptListPointer()); - } SizedScriptList *sizedScriptList() const { - MOZ_ASSERT(scriptListTag() == Multi); - return reinterpret_cast(scriptListPointer()); + return scriptList_; } unsigned numScripts() const { - ScriptListTag tag = scriptListTag(); - if (tag == Single) - return 1; - - if (tag < Multi) { - MOZ_ASSERT(int(tag) >= 2); - return static_cast(tag); - } - - return sizedScriptList()->size; + return scriptList_->size; } JSScript *getScript(unsigned idx) const { MOZ_ASSERT(idx < numScripts()); + return sizedScriptList()->pairs[idx].script; + } - ScriptListTag tag = scriptListTag(); - - if (tag == Single) - return singleScript(); - - if (tag < Multi) { - MOZ_ASSERT(int(tag) >= 2); - return rawScriptArray()[idx]; - } - - return sizedScriptList()->scripts[idx]; + const char *getStr(unsigned idx) const { + MOZ_ASSERT(idx < numScripts()); + return sizedScriptList()->pairs[idx].str; } void destroy(); @@ -230,27 +173,39 @@ class JitcodeGlobalEntry bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results, uint32_t *depth) const; + + uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results, + uint32_t maxResults) const; }; struct BaselineEntry : public BaseEntry { JSScript *script_; + const char *str_; - void init(void *nativeStartAddr, void *nativeEndAddr, JSScript *script) + void init(void *nativeStartAddr, void *nativeEndAddr, JSScript *script, const char *str) { MOZ_ASSERT(script != nullptr); BaseEntry::init(Baseline, nativeStartAddr, nativeEndAddr); script_ = script; + str_ = str; } JSScript *script() const { return script_; } - void destroy() {} + const char *str() const { + return str_; + } + + void destroy(); bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results, uint32_t *depth) const; + + uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results, + uint32_t maxResults) const; }; struct IonCacheEntry : public BaseEntry @@ -272,6 +227,33 @@ class JitcodeGlobalEntry bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results, uint32_t *depth) const; + + uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results, + uint32_t maxResults) const; + }; + + // Dummy entries are created for jitcode generated when profiling is not turned on, + // so that they have representation in the global table if they are on the + // stack when profiling is enabled. + struct DummyEntry : public BaseEntry + { + void init(void *nativeStartAddr, void *nativeEndAddr) { + BaseEntry::init(Dummy, nativeStartAddr, nativeEndAddr); + } + + void destroy() {} + + bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results, + uint32_t *depth) const + { + return true; + } + + uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results, + uint32_t maxResults) const + { + return 0; + } }; // QueryEntry is never stored in the table, just used for queries @@ -304,6 +286,9 @@ class JitcodeGlobalEntry // IonCache stubs. IonCacheEntry ionCache_; + // Dummy entries. + DummyEntry dummy_; + // When doing queries on the SplayTree for particular addresses, // the query addresses are representd using a QueryEntry. QueryEntry query_; @@ -326,6 +311,10 @@ class JitcodeGlobalEntry ionCache_ = ionCache; } + explicit JitcodeGlobalEntry(const DummyEntry &dummy) { + dummy_ = dummy; + } + explicit JitcodeGlobalEntry(const QueryEntry &query) { query_ = query; } @@ -347,6 +336,9 @@ class JitcodeGlobalEntry case IonCache: ionCacheEntry().destroy(); break; + case Dummy: + dummyEntry().destroy(); + break; case Query: queryEntry().destroy(); break; @@ -397,6 +389,9 @@ class JitcodeGlobalEntry bool isIonCache() const { return kind() == IonCache; } + bool isDummy() const { + return kind() == Dummy; + } bool isQuery() const { return kind() == Query; } @@ -413,6 +408,10 @@ class JitcodeGlobalEntry MOZ_ASSERT(isIonCache()); return ionCache_; } + DummyEntry &dummyEntry() { + MOZ_ASSERT(isDummy()); + return dummy_; + } QueryEntry &queryEntry() { MOZ_ASSERT(isQuery()); return query_; @@ -430,6 +429,10 @@ class JitcodeGlobalEntry MOZ_ASSERT(isIonCache()); return ionCache_; } + const DummyEntry &dummyEntry() const { + MOZ_ASSERT(isDummy()); + return dummy_; + } const QueryEntry &queryEntry() const { MOZ_ASSERT(isQuery()); return query_; @@ -450,6 +453,26 @@ class JitcodeGlobalEntry return baselineEntry().callStackAtAddr(rt, ptr, results, depth); case IonCache: return ionCacheEntry().callStackAtAddr(rt, ptr, results, depth); + case Dummy: + return dummyEntry().callStackAtAddr(rt, ptr, results, depth); + default: + MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); + } + return false; + } + + uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results, + uint32_t maxResults) const + { + switch (kind()) { + case Ion: + return ionEntry().callStackAtAddr(rt, ptr, results, maxResults); + case Baseline: + return baselineEntry().callStackAtAddr(rt, ptr, results, maxResults); + case IonCache: + return ionCacheEntry().callStackAtAddr(rt, ptr, results, maxResults); + case Dummy: + return dummyEntry().callStackAtAddr(rt, ptr, results, maxResults); default: MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); } @@ -462,6 +485,9 @@ class JitcodeGlobalEntry // Compare two global entries. static int compare(const JitcodeGlobalEntry &ent1, const JitcodeGlobalEntry &ent2); + + // Compute a profiling string for a given script. + static char *createScriptString(JSContext *cx, JSScript *script, size_t *length=nullptr); }; /* @@ -492,23 +518,26 @@ class JitcodeGlobalTable return tree_.empty(); } - bool lookup(void *ptr, JitcodeGlobalEntry *result); - void lookupInfallible(void *ptr, JitcodeGlobalEntry *result); + bool lookup(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt); + void lookupInfallible(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt); - bool addEntry(const JitcodeGlobalEntry::IonEntry &entry) { - return addEntry(JitcodeGlobalEntry(entry)); + bool addEntry(const JitcodeGlobalEntry::IonEntry &entry, JSRuntime *rt) { + return addEntry(JitcodeGlobalEntry(entry), rt); } - bool addEntry(const JitcodeGlobalEntry::BaselineEntry &entry) { - return addEntry(JitcodeGlobalEntry(entry)); + bool addEntry(const JitcodeGlobalEntry::BaselineEntry &entry, JSRuntime *rt) { + return addEntry(JitcodeGlobalEntry(entry), rt); } - bool addEntry(const JitcodeGlobalEntry::IonCacheEntry &entry) { - return addEntry(JitcodeGlobalEntry(entry)); + bool addEntry(const JitcodeGlobalEntry::IonCacheEntry &entry, JSRuntime *rt) { + return addEntry(JitcodeGlobalEntry(entry), rt); + } + bool addEntry(const JitcodeGlobalEntry::DummyEntry &entry, JSRuntime *rt) { + return addEntry(JitcodeGlobalEntry(entry), rt); } - void removeEntry(void *startAddr); + void removeEntry(void *startAddr, JSRuntime *rt); private: - bool addEntry(const JitcodeGlobalEntry &entry); + bool addEntry(const JitcodeGlobalEntry &entry, JSRuntime *rt); }; @@ -815,8 +844,8 @@ class JitcodeIonTable regionOffsets_[i] = 0; } - bool makeIonEntry(JSContext *cx, JitCode *code, uint32_t numScripts, JSScript **scripts, - JitcodeGlobalEntry::IonEntry &out); + bool makeIonEntry(JSContext *cx, JitCode *code, uint32_t numScripts, + JSScript **scripts, JitcodeGlobalEntry::IonEntry &out); uint32_t numRegions() const { return numRegions_; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 86d8dfab9860..678925d2c40f 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4164,8 +4164,10 @@ SingleStepCallback(void *arg, jit::Simulator *sim, void *pc) MOZ_ASSERT(i.stackAddress() != nullptr); MOZ_ASSERT(lastStackAddress <= i.stackAddress()); lastStackAddress = i.stackAddress(); - const char *label = i.label(); - stack.append(label, strlen(label)); + JS::ProfilingFrameIterator::Frame frames[16]; + uint32_t nframes = i.extractStack(frames, 0, 16); + for (uint32_t i = 0; i < nframes; i++) + stack.append(frames[i].label, strlen(frames[i].label)); } // Only append the stack if it differs from the last stack. diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 4539be97def5..fa9badcdaa3d 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1684,7 +1684,12 @@ CASE(JSOP_LOOPENTRY) goto error; if (status == jit::Method_Compiled) { bool wasSPS = REGS.fp()->hasPushedSPSFrame(); - jit::JitExecStatus maybeOsr = jit::EnterBaselineAtBranch(cx, REGS.fp(), REGS.pc); + + jit::JitExecStatus maybeOsr; + { + SPSBaselineOSRMarker spsOSR(cx->runtime(), wasSPS); + maybeOsr = jit::EnterBaselineAtBranch(cx, REGS.fp(), REGS.pc); + } // We failed to call into baseline at all, so treat as an error. if (maybeOsr == jit::JitExec_Aborted) diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 951996f7eed9..a23eccad1e2c 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -597,6 +597,9 @@ class PerThreadData : public PerThreadDataFriendFields void *addressOfProfilingActivation() { return (void*) &profilingActivation_; } + static unsigned offsetOfProfilingActivation() { + return offsetof(PerThreadData, profilingActivation_); + } js::AsmJSActivation *asmJSActivationStack() const { return asmJSActivationStack_; @@ -1029,7 +1032,7 @@ struct JSRuntime : public JS::shadow::Runtime, /* Whether sampling should be enabled or not. */ private: - bool suppressProfilerSampling; + mozilla::Atomic suppressProfilerSampling; public: bool isProfilerSamplingEnabled() const { diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index de970b4dc87c..459bc6efe29b 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -14,6 +14,7 @@ #include "jit/BaselineFrame.h" #include "jit/BaselineJIT.h" +#include "jit/JitFrameIterator.h" #include "jit/JitFrames.h" #include "vm/StringBuffer.h" @@ -209,18 +210,18 @@ SPSProfiler::exit(JSScript *script, JSFunction *maybeFun) } void -SPSProfiler::enterAsmJS(const char *string, void *sp) +SPSProfiler::beginPseudoJS(const char *string, void *sp) { /* these operations cannot be re-ordered, so volatile-ize operations */ volatile ProfileEntry *stack = stack_; volatile uint32_t *size = size_; uint32_t current = *size; - MOZ_ASSERT(enabled()); + MOZ_ASSERT(installed()); if (current < max_) { stack[current].setLabel(string); stack[current].setCppFrame(sp, 0); - stack[current].setFlag(ProfileEntry::ASMJS); + stack[current].setFlag(ProfileEntry::BEGIN_PSEUDO_JS); } *size = current + 1; } @@ -332,17 +333,51 @@ SPSEntryMarker::SPSEntryMarker(JSRuntime *rt, } size_before = *profiler->size_; // We want to push a CPP frame so the profiler can correctly order JS and native stacks. - profiler->push("js::RunScript", this, nullptr, nullptr, /* copy = */ false); + profiler->beginPseudoJS("js::RunScript", this); profiler->push("js::RunScript", nullptr, script, script->code(), /* copy = */ false); } SPSEntryMarker::~SPSEntryMarker() { - if (profiler != nullptr) { - profiler->pop(); - profiler->pop(); - MOZ_ASSERT(size_before == *profiler->size_); + if (profiler == nullptr) + return; + + profiler->pop(); + profiler->endPseudoJS(); + MOZ_ASSERT(size_before == *profiler->size_); +} + +SPSBaselineOSRMarker::SPSBaselineOSRMarker(JSRuntime *rt, bool hasSPSFrame + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) + : profiler(&rt->spsProfiler) +{ + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (!hasSPSFrame || !profiler->enabled()) { + profiler = nullptr; + return; } + + size_before = profiler->size(); + if (profiler->size() == 0) + return; + + ProfileEntry &entry = profiler->stack()[profiler->size() - 1]; + MOZ_ASSERT(entry.isJs()); + entry.setOSR(); +} + +SPSBaselineOSRMarker::~SPSBaselineOSRMarker() +{ + if (profiler == nullptr) + return; + + MOZ_ASSERT(size_before == *profiler->size_); + if (profiler->size() == 0) + return; + + ProfileEntry &entry = profiler->stack()[profiler->size() - 1]; + MOZ_ASSERT(entry.isJs()); + entry.unsetOSR(); } JS_FRIEND_API(jsbytecode*) @@ -384,8 +419,6 @@ js::ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip) return rt->spsProfiler.ipToPC(script, size_t(ip)); } - - AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : rt_(cx->runtime()), @@ -419,28 +452,7 @@ js::GetTopProfilingJitFrame(uint8_t *exitFramePtr) if (!exitFramePtr) return nullptr; - jit::ExitFrameLayout *exitFrame = (jit::ExitFrameLayout *) exitFramePtr; - size_t prevSize = exitFrame->prevFrameLocalSize(); - jit::FrameType prevType = exitFrame->prevType(); - - uint8_t *prev = exitFramePtr + (jit::ExitFrameLayout::Size() + prevSize); - - // previous frame type must be one of IonJS, BaselineJS, or BaselineStub, - // or unwound variants thereof. - switch (prevType) { - case jit::JitFrame_IonJS: - case jit::JitFrame_Unwound_IonJS: - case jit::JitFrame_BaselineJS: - return prev; - - case jit::JitFrame_BaselineStub: - case jit::JitFrame_Unwound_BaselineStub: { - void *framePtr = ((jit::BaselineStubFrameLayout *) prev)->reverseSavedFramePtr(); - return ((uint8_t *) framePtr) + jit::BaselineFrame::FramePointerOffset; - } - - default: - MOZ_CRASH("unknown callee token type"); - return nullptr; - } + jit::JitProfilingFrameIterator iter(exitFramePtr); + MOZ_ASSERT(!iter.done()); + return iter.fp(); } diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h index 173037e10182..ddba04b4ada1 100644 --- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -112,10 +112,12 @@ typedef HashMap, SystemAllocPol ProfileStringMap; class SPSEntryMarker; +class SPSBaselineOSRMarker; class SPSProfiler { friend class SPSEntryMarker; + friend class SPSBaselineOSRMarker; JSRuntime *rt; ProfileStringMap strings; @@ -151,6 +153,7 @@ class SPSProfiler uint32_t *sizePointer() { return size_; } uint32_t maxSize() { return max_; } + uint32_t size() { MOZ_ASSERT(installed()); return *size_; } ProfileEntry *stack() { return stack_; } /* management of whether instrumentation is on or off */ @@ -180,8 +183,8 @@ class SPSProfiler } /* Enter asm.js code */ - void enterAsmJS(const char *string, void *sp); - void exitAsmJS() { pop(); } + void beginPseudoJS(const char *string, void *sp); + void endPseudoJS() { pop(); } jsbytecode *ipToPC(JSScript *script, size_t ip) { return nullptr; } @@ -271,6 +274,24 @@ class SPSEntryMarker MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; +/* + * This class is used in the interpreter to bound regions where the baseline JIT + * being entered via OSR. It marks the current top pseudostack entry as + * OSR-ed + */ +class SPSBaselineOSRMarker +{ + public: + explicit SPSBaselineOSRMarker(JSRuntime *rt, bool hasSPSFrame + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + ~SPSBaselineOSRMarker(); + + private: + SPSProfiler *profiler; + mozilla::DebugOnly size_before; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + /* * SPS is the profiling backend used by the JS engine to enable time profiling. * More information can be found in vm/SPSProfiler.{h,cpp}. This class manages diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index d8c3fd8b3fdc..d9b55daa954e 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -14,6 +14,7 @@ #include "asmjs/AsmJSModule.h" #include "gc/Marking.h" #include "jit/BaselineFrame.h" +#include "jit/JitcodeMap.h" #include "jit/JitCompartment.h" #include "js/GCAPI.h" #include "vm/Opcodes.h" @@ -1392,6 +1393,8 @@ jit::JitActivation::JitActivation(JSContext *cx, bool active) prevJitActivation_ = cx->mainThread().jitActivation; cx->mainThread().jitJSContext = cx; cx->mainThread().jitActivation = this; + + registerProfiling(); } else { prevJitTop_ = nullptr; prevJitJSContext_ = nullptr; @@ -1402,6 +1405,9 @@ jit::JitActivation::JitActivation(JSContext *cx, bool active) jit::JitActivation::~JitActivation() { if (active_) { + if (isProfiling()) + unregisterProfiling(); + cx_->perThreadData->jitTop = prevJitTop_; cx_->perThreadData->jitJSContext = prevJitJSContext_; cx_->perThreadData->jitActivation = prevJitActivation_; @@ -1418,6 +1424,13 @@ jit::JitActivation::~JitActivation() js_delete(rematerializedFrames_); } +bool +jit::JitActivation::isProfiling() const +{ + // All JitActivations can be profiled. + return true; +} + void jit::JitActivation::setBailoutData(jit::BailoutFrameInfo *bailoutData) { @@ -1442,18 +1455,25 @@ jit::JitActivation::setActive(JSContext *cx, bool active) // (Not tested and will probably fail in other situations.) MOZ_ASSERT(cx->mainThread().activation_ == this); MOZ_ASSERT(active != active_); - active_ = active; if (active) { + *((volatile bool *) active_) = true; prevJitTop_ = cx->mainThread().jitTop; prevJitJSContext_ = cx->mainThread().jitJSContext; prevJitActivation_ = cx->mainThread().jitActivation; cx->mainThread().jitJSContext = cx; cx->mainThread().jitActivation = this; + + registerProfiling(); + } else { + unregisterProfiling(); + cx->mainThread().jitTop = prevJitTop_; cx->mainThread().jitJSContext = prevJitJSContext_; cx->mainThread().jitActivation = prevJitActivation_; + + *((volatile bool *) active_) = false; } } @@ -1600,10 +1620,8 @@ AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module) // NB: this is a hack and can be removed once Ion switches over to // JS::ProfilingFrameIterator. - if (cx->runtime()->spsProfiler.enabled()) { + if (cx->runtime()->spsProfiler.enabled()) profiler_ = &cx->runtime()->spsProfiler; - profiler_->enterAsmJS("asm.js code :0", this); - } prevAsmJSForModule_ = module.activation(); module.activation() = this; @@ -1621,9 +1639,6 @@ AsmJSActivation::~AsmJSActivation() // Hide this activation from the profiler before is is destroyed. unregisterProfiling(); - if (profiler_) - profiler_->exitAsmJS(); - MOZ_ASSERT(fp_ == nullptr); MOZ_ASSERT(module_.activation() == this); @@ -1663,7 +1678,13 @@ Activation::unregisterProfiling() { MOZ_ASSERT(isProfiling()); MOZ_ASSERT(cx_->perThreadData->profilingActivation_ == this); - cx_->perThreadData->profilingActivation_ = prevProfiling_; + + // There may be a non-active jit activation in the linked list. Skip past it. + Activation *prevProfiling = prevProfiling_; + while (prevProfiling && prevProfiling->isJit() && !prevProfiling->asJit()->isActive()) + prevProfiling = prevProfiling->prevProfiling_; + + cx_->perThreadData->profilingActivation_ = prevProfiling; } ActivationIterator::ActivationIterator(JSRuntime *rt) @@ -1701,14 +1722,24 @@ ActivationIterator::settle() } JS::ProfilingFrameIterator::ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state) - : activation_(rt->mainThread.profilingActivation()) + : rt_(rt), + activation_(rt->mainThread.profilingActivation()), + savedPrevJitTop_(nullptr) { if (!activation_) return; + // If profiler sampling is not enabled, skip. + if (!rt_->isProfilerSamplingEnabled()) { + activation_ = nullptr; + return; + } + MOZ_ASSERT(activation_->isProfiling()); - static_assert(sizeof(AsmJSProfilingFrameIterator) <= StorageSpace, "Need to increase storage"); + static_assert(sizeof(AsmJSProfilingFrameIterator) <= StorageSpace && + sizeof(jit::JitProfilingFrameIterator) <= StorageSpace, + "Need to increase storage"); iteratorConstruct(state); settle(); @@ -1726,9 +1757,15 @@ void JS::ProfilingFrameIterator::operator++() { MOZ_ASSERT(!done()); + MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit()); - MOZ_ASSERT(activation_->isAsmJS()); - ++asmJSIter(); + if (activation_->isAsmJS()) { + ++asmJSIter(); + settle(); + return; + } + + ++jitIter(); settle(); } @@ -1738,6 +1775,11 @@ JS::ProfilingFrameIterator::settle() while (iteratorDone()) { iteratorDestroy(); activation_ = activation_->prevProfiling(); + + // Skip past any non-active jit activations in the list. + while (activation_ && activation_->isJit() && !activation_->asJit()->isActive()) + activation_ = activation_->prevProfiling(); + if (!activation_) return; iteratorConstruct(); @@ -1748,52 +1790,134 @@ void JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState &state) { MOZ_ASSERT(!done()); + MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit()); - MOZ_ASSERT(activation_->isAsmJS()); - new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS(), state); + if (activation_->isAsmJS()) { + new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS(), state); + // Set savedPrevJitTop_ to the actual jitTop_ from the runtime. + savedPrevJitTop_ = activation_->cx()->perThreadData->jitTop; + return; + } + + MOZ_ASSERT(activation_->asJit()->isActive()); + new (storage_.addr()) jit::JitProfilingFrameIterator(rt_, state); } void JS::ProfilingFrameIterator::iteratorConstruct() { MOZ_ASSERT(!done()); + MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit()); - MOZ_ASSERT(activation_->isAsmJS()); - new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS()); + if (activation_->isAsmJS()) { + new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS()); + return; + } + + MOZ_ASSERT(activation_->asJit()->isActive()); + MOZ_ASSERT(savedPrevJitTop_ != nullptr); + new (storage_.addr()) jit::JitProfilingFrameIterator(savedPrevJitTop_); } void JS::ProfilingFrameIterator::iteratorDestroy() { MOZ_ASSERT(!done()); + MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit()); - MOZ_ASSERT(activation_->isAsmJS()); - asmJSIter().~AsmJSProfilingFrameIterator(); + if (activation_->isAsmJS()) { + asmJSIter().~AsmJSProfilingFrameIterator(); + return; + } + + // Save prevjitTop for later use + savedPrevJitTop_ = activation_->asJit()->prevJitTop(); + jitIter().~JitProfilingFrameIterator(); } bool JS::ProfilingFrameIterator::iteratorDone() { MOZ_ASSERT(!done()); + MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit()); - MOZ_ASSERT(activation_->isAsmJS()); - return asmJSIter().done(); + if (activation_->isAsmJS()) + return asmJSIter().done(); + + return jitIter().done(); } void * JS::ProfilingFrameIterator::stackAddress() const { MOZ_ASSERT(!done()); + MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit()); - MOZ_ASSERT(activation_->isAsmJS()); - return asmJSIter().stackAddress(); + if (activation_->isAsmJS()) + return asmJSIter().stackAddress(); + + return jitIter().stackAddress(); } -const char * -JS::ProfilingFrameIterator::label() const +uint32_t +JS::ProfilingFrameIterator::extractStack(Frame *frames, uint32_t offset, uint32_t end) const +{ + if (offset >= end) + return 0; + + void *stackAddr = stackAddress(); + + if (isAsmJS()) { + frames[offset].kind = Frame_AsmJS; + frames[offset].stackAddress = stackAddr; + frames[offset].returnAddress = nullptr; + frames[offset].activation = activation_; + frames[offset].label = asmJSIter().label(); + return 1; + } + + MOZ_ASSERT(isJit()); + void *returnAddr = jitIter().returnAddressToFp(); + + // Look up an entry for the return address. + jit::JitcodeGlobalTable *table = rt_->jitRuntime()->getJitcodeGlobalTable(); + jit::JitcodeGlobalEntry entry; + mozilla::DebugOnly result = table->lookup(returnAddr, &entry, rt_); + MOZ_ASSERT(result); + + MOZ_ASSERT(entry.isIon() || entry.isIonCache() || entry.isBaseline() || entry.isDummy()); + + // Dummy frames produce no stack frames. + if (entry.isDummy()) + return 0; + + FrameKind kind = entry.isBaseline() ? Frame_Baseline : Frame_Ion; + + // Extract the stack for the entry. Assume maximum inlining depth is <64 + const char *labels[64]; + uint32_t depth = entry.callStackAtAddr(rt_, returnAddr, labels, 64); + MOZ_ASSERT(depth < 64); + for (uint32_t i = 0; i < depth; i++) { + if (offset + i >= end) + return i; + frames[offset + i].kind = kind; + frames[offset + i].stackAddress = stackAddr; + frames[offset + i].returnAddress = returnAddr; + frames[offset + i].activation = activation_; + frames[offset + i].label = labels[i]; + } + return depth; +} + +bool +JS::ProfilingFrameIterator::isAsmJS() const { MOZ_ASSERT(!done()); - - MOZ_ASSERT(activation_->isAsmJS()); - return asmJSIter().label(); + return activation_->isAsmJS(); +} + +bool +JS::ProfilingFrameIterator::isJit() const +{ + return activation_->isJit(); } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 99211a2f6c65..da18a3911773 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -1131,6 +1131,10 @@ class Activation return hideScriptedCallerCount_ > 0; } + static size_t offsetOfPrevProfiling() { + return offsetof(Activation, prevProfiling_); + } + private: Activation(const Activation &other) = delete; void operator=(const Activation &other) = delete; @@ -1300,9 +1304,7 @@ class JitActivation : public Activation } void setActive(JSContext *cx, bool active = true); - bool isProfiling() const { - return false; - } + bool isProfiling() const; uint8_t *prevJitTop() const { return prevJitTop_; diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp index 984cc7310a15..652eec7dedbc 100644 --- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -391,9 +391,9 @@ static void addPseudoEntry(volatile StackEntry &entry, ThreadProfile &aProfile, PseudoStack *stack, void *lastpc) { - // Pseudo-frames with the ASMJS flag are just annotations and should not be - // recorded in the profile. - if (entry.hasFlag(StackEntry::ASMJS)) + // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations + // and should not be recorded in the profile. + if (entry.hasFlag(StackEntry::BEGIN_PSEUDO_JS)) return; int lineno = -1; @@ -455,10 +455,19 @@ struct NativeStack size_t count; }; -struct JSFrame -{ - void* stackAddress; - const char* label; +mozilla::Atomic WALKING_JS_STACK(false); + +struct AutoWalkJSStack { + bool walkAllowed; + + AutoWalkJSStack() : walkAllowed(false) { + walkAllowed = WALKING_JS_STACK.compareExchange(false, true); + } + + ~AutoWalkJSStack() { + if (walkAllowed) + WALKING_JS_STACK = false; + } }; static @@ -472,20 +481,28 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native // like the native stack, the JS stack is iterated youngest-to-oldest and we // need to iterate oldest-to-youngest when adding entries to aProfile. - JSFrame jsFrames[1000]; uint32_t jsCount = 0; - if (aSample && pseudoStack->mRuntime) { - JS::ProfilingFrameIterator::RegisterState registerState; - registerState.pc = aSample->pc; - registerState.sp = aSample->sp; + JS::ProfilingFrameIterator::Frame jsFrames[1000]; + { + AutoWalkJSStack autoWalkJSStack; + const uint32_t maxFrames = mozilla::ArrayLength(jsFrames); + + if (aSample && pseudoStack->mRuntime && autoWalkJSStack.walkAllowed) { + JS::ProfilingFrameIterator::RegisterState registerState; + registerState.pc = aSample->pc; + registerState.sp = aSample->sp; #ifdef ENABLE_ARM_LR_SAVING - registerState.lr = aSample->lr; + registerState.lr = aSample->lr; #endif - JS::ProfilingFrameIterator jsIter(pseudoStack->mRuntime, registerState); - for (; jsCount < mozilla::ArrayLength(jsFrames) && !jsIter.done(); ++jsCount, ++jsIter) { - jsFrames[jsCount].stackAddress = jsIter.stackAddress(); - jsFrames[jsCount].label = jsIter.label(); + JS::ProfilingFrameIterator jsIter(pseudoStack->mRuntime, registerState); + for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) { + uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames); + MOZ_ASSERT(extracted <= (maxFrames - jsCount)); + jsCount += extracted; + if (jsCount == maxFrames) + break; + } } } @@ -501,87 +518,72 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native int32_t jsIndex = jsCount - 1; int32_t nativeIndex = aNativeStack.count - 1; + uint8_t *lastPseudoCppStackAddr = nullptr; + // Iterate as long as there is at least one frame remaining. while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) { - // There are 1 to 3 frames available. Find and add the oldest. Handle pseudo - // frames first, since there are two special cases that must be considered - // before everything else. + // There are 1 to 3 frames available. Find and add the oldest. + + uint8_t *pseudoStackAddr = nullptr; + uint8_t *jsStackAddr = nullptr; + uint8_t *nativeStackAddr = nullptr; + if (pseudoIndex != pseudoCount) { volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex]; - // isJs pseudo-stack frames assume the stackAddress of the preceding isCpp - // pseudo-stack frame. If we arrive at an isJs pseudo frame, we've already - // encountered the preceding isCpp stack frame and it was oldest, we can - // assume the isJs frame is oldest without checking other frames. - if (pseudoFrame.isJs()) { - addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr); + if (pseudoFrame.isCpp()) + lastPseudoCppStackAddr = (uint8_t *) pseudoFrame.stackAddress(); + + // Skip any pseudo-stack JS frames which are marked isOSR + // Pseudostack frames are marked isOSR when the JS interpreter + // enters a jit frame on a loop edge (via on-stack-replacement, + // or OSR). To avoid both the pseudoframe and jit frame being + // recorded (and showing up twice), the interpreter marks the + // interpreter pseudostack entry with the OSR flag to ensure that + // it doesn't get counted. + if (pseudoFrame.isJs() && pseudoFrame.isOSR()) { pseudoIndex++; continue; } - // Currently, only asm.js frames use the JS stack and Ion/Baseline/Interp - // frames use the pseudo stack. In the optimized asm.js->Ion call path, no - // isCpp frame is pushed, leading to the callstack: - // old | pseudo isCpp | asm.js | pseudo isJs | new - // Since there is no interleaving isCpp pseudo frame between the asm.js - // and isJs pseudo frame, the above isJs logic will render the callstack: - // old | pseudo isCpp | pseudo isJs | asm.js | new - // which is wrong. To deal with this, a pseudo isCpp frame pushed right - // before entering asm.js flagged with StackEntry::ASMJS. When we see this - // flag, we first push all the asm.js frames (up to the next frame with a - // stackAddress) before pushing the isJs frames. There is no Ion->asm.js - // fast path, so we don't have to worry about asm.js->Ion->asm.js. - // - // (This and the above isJs special cases can be removed once all JS - // execution modes switch from the pseudo stack to the JS stack.) - if (pseudoFrame.hasFlag(StackEntry::ASMJS)) { - void *stopStackAddress = nullptr; - for (uint32_t i = pseudoIndex + 1; i != pseudoCount; i++) { - if (pseudoFrames[i].isCpp()) { - stopStackAddress = pseudoFrames[i].stackAddress(); - break; - } - } - - if (nativeIndex >= 0) { - stopStackAddress = std::max(stopStackAddress, aNativeStack.sp_array[nativeIndex]); - } - - while (jsIndex >= 0 && jsFrames[jsIndex].stackAddress > stopStackAddress) { - addDynamicTag(aProfile, 'c', jsFrames[jsIndex].label); - jsIndex--; - } - - pseudoIndex++; - continue; - } - - // Finally, consider the normal case of a plain C++ pseudo-frame. - if ((jsIndex < 0 || pseudoFrame.stackAddress() > jsFrames[jsIndex].stackAddress) && - (nativeIndex < 0 || pseudoFrame.stackAddress() > aNativeStack.sp_array[nativeIndex])) - { - // The (C++) pseudo-frame is the oldest. - addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr); - pseudoIndex++; - continue; - } + MOZ_ASSERT(lastPseudoCppStackAddr); + pseudoStackAddr = lastPseudoCppStackAddr; } - if (jsIndex >= 0) { - // Test whether the JS frame is the oldest. - JSFrame &jsFrame = jsFrames[jsIndex]; - if ((pseudoIndex == pseudoCount || jsFrame.stackAddress > pseudoFrames[pseudoIndex].stackAddress()) && - (nativeIndex < 0 || jsFrame.stackAddress > aNativeStack.sp_array[nativeIndex])) - { - // The JS frame is the oldest. - addDynamicTag(aProfile, 'c', jsFrame.label); - jsIndex--; - continue; - } + if (jsIndex >= 0) + jsStackAddr = (uint8_t *) jsFrames[jsIndex].stackAddress; + + if (nativeIndex >= 0) + nativeStackAddr = (uint8_t *) aNativeStack.sp_array[nativeIndex]; + + // Sanity checks. + MOZ_ASSERT_IF(pseudoStackAddr, pseudoStackAddr != jsStackAddr && + pseudoStackAddr != nativeStackAddr); + MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr && + jsStackAddr != nativeStackAddr); + MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr && + nativeStackAddr != jsStackAddr); + + // Check to see if pseudoStack frame is top-most. + if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) { + MOZ_ASSERT(pseudoIndex < pseudoCount); + volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex]; + addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr); + pseudoIndex++; + continue; } - // If execution reaches this point, there must be a native frame and it must - // be the oldest. + // Check to see if JS jit stack frame is top-most + if (jsStackAddr > nativeStackAddr) { + MOZ_ASSERT(jsIndex >= 0); + addDynamicTag(aProfile, 'c', jsFrames[jsIndex].label); + jsIndex--; + continue; + } + + // If we reach here, there must be a native stack entry and it must be the + // greatest entry. + MOZ_ASSERT(nativeStackAddr); MOZ_ASSERT(nativeIndex >= 0); aProfile.addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex])); nativeIndex--; @@ -737,6 +739,7 @@ void doSampleStackTrace(ThreadProfile &aProfile, TickSample *aSample, bool aAddL void TableTicker::Tick(TickSample* sample) { + // Don't allow for ticks to happen within other ticks. if (HasUnwinderThread()) { UnwinderTick(sample); } else { From 73ec9a8625304ec03fc6f23f70391d19ca974975 Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 15 Jan 2015 20:11:22 -0500 Subject: [PATCH 050/133] Bug 1057082 - 7/7 - Fix tests. r=jandem --- js/src/jit-test/tests/asm.js/testProfiling.js | 84 ++++++++++++++----- js/src/jit/JitcodeMap.cpp | 2 +- js/src/shell/js.cpp | 7 +- .../server/tests/unit/test_profiler_data.js | 3 +- tools/profiler/tests/test_enterjit_osr.js | 48 ++++------- 5 files changed, 87 insertions(+), 57 deletions(-) diff --git a/js/src/jit-test/tests/asm.js/testProfiling.js b/js/src/jit-test/tests/asm.js/testProfiling.js index e1e2c1dc0d81..9c280741b395 100644 --- a/js/src/jit-test/tests/asm.js/testProfiling.js +++ b/js/src/jit-test/tests/asm.js/testProfiling.js @@ -5,15 +5,50 @@ load(libdir + "asserts.js"); if (!getBuildConfiguration()["arm-simulator"]) quit(); -function assertEqualStacks(got, expect) +function checkSubSequence(got, expect) { - // Strip off the " (script/library info)" - got = String(got).replace(/ \([^\)]*\)/g, ""); + var got_i = 0; + EXP: for (var exp_i = 0; exp_i < expect.length; exp_i++) { + var item = expect[exp_i]; + // Scan for next match in got. + while (got_i < got.length) { + if (got[got_i++] == expect[exp_i]) + continue EXP; + } + print("MISMATCH: " + got.join(",") + "\n" + + " VS " + expect.join(",")); + return false; + } + return true; +} - // Shorten FFI/entry trampolines - got = got.replace(/(fast|slow) FFI trampoline/g, "<").replace(/entry trampoline/g, ">"); +function assertStackContainsSeq(got, expect) +{ + var normalized = []; - assertEq(got, expect); + for (var i = 0; i < got.length; i++) { + if (got[i].length == 0) + continue; + var parts = got[i].split(','); + for (var j = 0; j < parts.length; j++) { + var frame = parts[j]; + frame = frame.replace(/ \([^\)]*\)/g, ""); + frame = frame.replace(/(fast|slow) FFI trampoline/g, "<"); + frame = frame.replace(/entry trampoline/g, ">"); + frame = frame.replace(/(\/[^\/,<]+)*\/testProfiling.js/g, ""); + frame = frame.replace(/testBuiltinD2D/g, ""); + frame = frame.replace(/testBuiltinF2F/g, ""); + frame = frame.replace(/testBuiltinDD2D/g, ""); + frame = frame.replace(/assertThrowsInstanceOf/g, ""); + frame = frame.replace(/^ffi[12]?/g, ""); + normalized.push(frame); + } + } + + var gotNorm = normalized.join(',').replace(/,+/g, ","); + gotNorm = gotNorm.replace(/^,/, "").replace(/,$/, ""); + + assertEq(checkSubSequence(gotNorm.split(','), expect.split(',')), true); } // Test profiling enablement while asm.js is running. @@ -28,15 +63,15 @@ var ffi = function(enable) { } var f = asmLink(asmCompile('global','ffis',USE_ASM + "var ffi=ffis.ffi; function g(i) { i=i|0; ffi(i|0) } function f(i) { i=i|0; g(i|0) } return f"), null, {ffi}); f(0); -assertEqualStacks(stacks, ""); +assertStackContainsSeq(stacks, "", true); f(+1); -assertEqualStacks(stacks, ""); +assertStackContainsSeq(stacks, "", true); f(0); -assertEqualStacks(stacks, ""); +assertStackContainsSeq(stacks, "<,g,f,>", true); f(-1); -assertEqualStacks(stacks, ""); +assertStackContainsSeq(stacks, "<,g,f,>", true); f(0); -assertEqualStacks(stacks, ""); +assertStackContainsSeq(stacks, "", true); // Enable profiling for the rest of the tests. enableSPSProfiling(); @@ -45,27 +80,27 @@ var f = asmLink(asmCompile(USE_ASM + "function f() { return 42 } return f")); enableSingleStepProfiling(); assertEq(f(), 42); var stacks = disableSingleStepProfiling(); -assertEqualStacks(stacks, ",>,f>,>,"); +assertStackContainsSeq(stacks, ">,f,>,>"); var f = asmLink(asmCompile(USE_ASM + "function g(i) { i=i|0; return (i+1)|0 } function f() { return g(42)|0 } return f")); enableSingleStepProfiling(); assertEq(f(), 43); var stacks = disableSingleStepProfiling(); -assertEqualStacks(stacks, ",>,f>,gf>,f>,>,"); +assertStackContainsSeq(stacks, ">,f,>,g,f,>,f,>,>"); var f = asmLink(asmCompile(USE_ASM + "function g1() { return 1 } function g2() { return 2 } function f(i) { i=i|0; return TBL[i&1]()|0 } var TBL=[g1,g2]; return f")); enableSingleStepProfiling(); assertEq(f(0), 1); assertEq(f(1), 2); var stacks = disableSingleStepProfiling(); -assertEqualStacks(stacks, ",>,f>,g1f>,f>,>,,>,f>,g2f>,f>,>,"); +assertStackContainsSeq(stacks, ">,f,>,g1,f,>,f,>,>,>,f,>,g2,f,>,f,>,>"); function testBuiltinD2D(name) { var f = asmLink(asmCompile('g', USE_ASM + "var fun=g.Math." + name + "; function f(d) { d=+d; return +fun(d) } return f"), this); enableSingleStepProfiling(); assertEq(f(.1), eval("Math." + name + "(.1)")); var stacks = disableSingleStepProfiling(); - assertEqualStacks(stacks, ",>,f>,Math." + name + "f>,f>,>,"); + assertStackContainsSeq(stacks, ">,f,>,Math." + name + ",f,>,f,>,>"); } for (name of ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'ceil', 'floor', 'exp', 'log']) testBuiltinD2D(name); @@ -74,7 +109,7 @@ function testBuiltinF2F(name) { enableSingleStepProfiling(); assertEq(f(.1), eval("Math.fround(Math." + name + "(Math.fround(.1)))")); var stacks = disableSingleStepProfiling(); - assertEqualStacks(stacks, ",>,f>,Math." + name + "f>,f>,>,"); + assertStackContainsSeq(stacks, ">,f,>,Math." + name + ",f,>,f,>,>"); } for (name of ['ceil', 'floor']) testBuiltinF2F(name); @@ -83,7 +118,7 @@ function testBuiltinDD2D(name) { enableSingleStepProfiling(); assertEq(f(.1, .2), eval("Math." + name + "(.1, .2)")); var stacks = disableSingleStepProfiling(); - assertEqualStacks(stacks, ",>,f>,Math." + name + "f>,f>,>,"); + assertStackContainsSeq(stacks, ">,f,>,Math." + name + ",f,>,f,>,>"); } for (name of ['atan2', 'pow']) testBuiltinDD2D(name); @@ -100,14 +135,14 @@ var f = asmLink(asmCompile('g','ffis', USE_ASM + "var ffi1=ffis.ffi1, ffi2=ffis. enableSingleStepProfiling(); assertEq(f(), 83); var stacks = disableSingleStepProfiling(); -assertEqualStacks(stacks, ",>,f>,,f>,,f>,>,"); -// Ion FFI exit +assertStackContainsSeq(stacks, ">,f,>,<,f,>,f,>,<,f,>,f,>,>"); + for (var i = 0; i < 20; i++) assertEq(f(), 83); enableSingleStepProfiling(); assertEq(f(), 83); var stacks = disableSingleStepProfiling(); -assertEqualStacks(stacks, ",>,f>,,f>,,f>,>,"); +assertStackContainsSeq(stacks, ">,f,>,<,f,>,f,>,<,f,>,f,>,>"); var ffi1 = function() { return 15 } var ffi2 = function() { return f2() + 17 } @@ -116,14 +151,17 @@ var {f1,f2} = asmLink(asmCompile('g','ffis', USE_ASM + "var ffi1=ffis.ffi1, ffi2 enableSingleStepProfiling(); assertEq(f1(), 32); var stacks = disableSingleStepProfiling(); -assertEqualStacks(stacks, ",>,f1>,,>,f2>,,f2>,>,,f1>,>,"); +assertStackContainsSeq(stacks, ">,f1,>,<,f1,>,>,<,f1,>,f2,>,<,f1,>,<,f2,>,<,f1,>,f2,>,<,f1,>,>,<,f1,>,<,f1,>,f1,>,>"); + + // Ion FFI exit for (var i = 0; i < 20; i++) assertEq(f1(), 32); enableSingleStepProfiling(); assertEq(f1(), 32); var stacks = disableSingleStepProfiling(); -assertEqualStacks(stacks, ",>,f1>,,>,f2>,,f2>,>,,f1>,>,"); +assertStackContainsSeq(stacks, ">,f1,>,<,f1,>,>,<,f1,>,f2,>,<,f1,>,<,f2,>,<,f1,>,f2,>,<,f1,>,>,<,f1,>,<,f1,>,f1,>,>"); + // Detachment exit var buf = new ArrayBuffer(BUF_CHANGE_MIN); @@ -132,7 +170,7 @@ var f = asmLink(asmCompile('g','ffis','buf', USE_ASM + 'var ffi = ffis.ffi; var enableSingleStepProfiling(); assertThrowsInstanceOf(f, InternalError); var stacks = disableSingleStepProfiling(); -assertEqualStacks(stacks, ",>,f>,,inline stubf>,,inline stubf>,"); +assertStackContainsSeq(stacks, ">,f,>,<,f,>,inline stub,f,>,<,f,>,inline stub,f,>"); // This takes forever to run. // Stack-overflow exit test diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index 01655c149c45..8c65861212fa 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -147,7 +147,7 @@ JitcodeGlobalEntry::BaselineEntry::destroy() { if (!str_) return; - js_free(str_); + js_free((void*) str_); str_ = nullptr; } diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 678925d2c40f..cf5803b575b9 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4160,14 +4160,19 @@ SingleStepCallback(void *arg, jit::Simulator *sim, void *pc) DebugOnly lastStackAddress = nullptr; StackChars stack; + uint32_t frameNo = 0; for (JS::ProfilingFrameIterator i(rt, state); !i.done(); ++i) { MOZ_ASSERT(i.stackAddress() != nullptr); MOZ_ASSERT(lastStackAddress <= i.stackAddress()); lastStackAddress = i.stackAddress(); JS::ProfilingFrameIterator::Frame frames[16]; uint32_t nframes = i.extractStack(frames, 0, 16); - for (uint32_t i = 0; i < nframes; i++) + for (uint32_t i = 0; i < nframes; i++) { + if (frameNo > 0) + stack.append(",", 1); stack.append(frames[i].label, strlen(frames[i].label)); + frameNo++; + } } // Only append the stack if it differs from the last stack. diff --git a/toolkit/devtools/server/tests/unit/test_profiler_data.js b/toolkit/devtools/server/tests/unit/test_profiler_data.js index 6ebb338e7afa..e58a9d33bcf8 100644 --- a/toolkit/devtools/server/tests/unit/test_profiler_data.js +++ b/toolkit/devtools/server/tests/unit/test_profiler_data.js @@ -106,12 +106,11 @@ function test_data(client, actor, callback) // Now check the samples. At least one sample is expected to // have been in the busy wait above. let loc = stack.name + " (" + stack.filename + ":" + funcLine + ")"; - let line = stack.lineNumber; do_check_true(response.profile.threads[0].samples.some(sample => { return typeof sample.frames == "object" && sample.frames.length != 0 && - sample.frames.some(f => (f.line == line) && (f.location == loc)); + sample.frames.some(f => (f.location == loc)); })); callback(); diff --git a/tools/profiler/tests/test_enterjit_osr.js b/tools/profiler/tests/test_enterjit_osr.js index 2595b28ec8d1..886f66008c74 100644 --- a/tools/profiler/tests/test_enterjit_osr.js +++ b/tools/profiler/tests/test_enterjit_osr.js @@ -5,10 +5,10 @@ function run_test() { let p = Cc["@mozilla.org/tools/profiler;1"]; // Just skip the test if the profiler component isn't present. if (!p) - return; + return; p = p.getService(Ci.nsIProfiler); if (!p) - return; + return; // This test assumes that it's starting on an empty SPS stack. // (Note that the other profiler tests also assume the profiler @@ -24,12 +24,12 @@ function run_test() { var delayMS = 5; while (1) { do_print("loop: ms = " + delayMS); - let then = Date.now(); - do { - let n = 10000; - while (--n); // OSR happens here - // Spin in the hope of getting a sample. - } while (Date.now() - then < delayMS); + let then = Date.now(); + do { + let n = 10000; + while (--n); // OSR happens here + // Spin in the hope of getting a sample. + } while (Date.now() - then < delayMS); let pr = p.getProfileData().threads[0].samples; if (pr.length > 0 || delayMS > 30000) return pr; @@ -41,30 +41,18 @@ function run_test() { do_check_neq(profile.length, 0); let stack = profile[profile.length - 1].frames.map(f => f.location); - stack = stack.slice(stack.lastIndexOf("js::RunScript") + 1); - do_print(stack); - // This test needs to not break on platforms and configurations - // where IonMonkey isn't available / enabled. - if (stack.length < 2 || stack[1] != "EnterJIT") { - do_print("No JIT?"); - // Try to check what we can.... - do_check_eq(Math.min(stack.length, 1), 1); - let thisInterp = stack[0]; - do_check_eq(thisInterp.split(" ")[0], "arbitrary_name"); - if (stack.length >= 2) { - let nextFrame = stack[1]; - do_check_neq(nextFrame.split(" ")[0], "arbitrary_name"); - } - } else { - do_check_eq(Math.min(stack.length, 3), 3); - let thisInterp = stack[0]; - let enterJit = stack[1]; - let thisBC = stack[2]; - do_check_eq(thisInterp.split(" ")[0], "arbitrary_name"); - do_check_eq(enterJit, "EnterJIT"); - do_check_eq(thisBC.split(" ")[0], "arbitrary_name"); + + // All we can really check here is ensure that there is exactly + // one arbitrary_name frame in the list. + var gotName = false; + for (var i = 0; i < stack.length; i++) { + if (stack[i].match(/arbitrary_name/)) { + do_check_eq(gotName, false); + gotName = true; + } } + do_check_eq(gotName, true); p.StopProfiler(); } From 3c3096880e58c64f84d058ea7cf9d2ecfdab6395 Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Fri, 16 Jan 2015 11:57:24 -0500 Subject: [PATCH 051/133] Bug 1057082 - Fix static-analysis build sailure on CLOSED TREE. r=tbpl-red --- js/src/jit/LIR-Common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 9bb35c1da27b..d58c97dac0f7 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -3675,7 +3675,7 @@ class LOsrEntry : public LInstructionHelper<1, 0, 1> public: LIR_HEADER(OsrEntry) - LOsrEntry(const LDefinition &temp) + explicit LOsrEntry(const LDefinition &temp) : frameDepth_(0) { setTemp(0, temp); From 4504972ef2887215a2a2bbddebb5ddce86560c21 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Fri, 16 Jan 2015 09:30:43 -0800 Subject: [PATCH 052/133] Bug 1121263 - Bump the leak threshold again for test_ipc. r=RyanVM CLOSED TREE --- testing/mochitest/mochitest_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py index ec88b81302e5..8b8c649d4509 100644 --- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -668,7 +668,7 @@ class MochitestOptions(optparse.OptionParser): # Bug 1121539 - OSX-only intermittent tab process leak in test_ipc.html if mozinfo.isMac: - options.leakThresholds["tab"] = 50000 + options.leakThresholds["tab"] = 100000 return options From dfe84c8ed858f52fd295a9d098a2e5dd08c02cc8 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Thu, 15 Jan 2015 22:11:14 -0500 Subject: [PATCH 053/133] bug 1121706 - don't offer h2 in alpn if w/out mandatory suite r=hurley --HG-- extra : rebase_source : 6220a4ace1df2f6cc7f02c98f46b331b251a42d2 --- netwerk/protocol/http/Http2Session.cpp | 5 +++++ netwerk/protocol/http/nsHttpHandler.cpp | 14 ++++++++++++++ netwerk/protocol/http/nsHttpHandler.h | 2 ++ netwerk/test/unit/test_http2.js | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index beff723f1095..d60bdde62e99 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -3283,6 +3283,11 @@ Http2Session::BufferOutput(const char *buf, bool // static Http2Session::ALPNCallback(nsISupports *securityInfo) { + if (!gHttpHandler->IsH2MandatorySuiteEnabled()) { + LOG3(("Http2Session::ALPNCallback Mandatory Cipher Suite Unavailable\n")); + return false; + } + nsCOMPtr ssl = do_QueryInterface(securityInfo); LOG3(("Http2Session::ALPNCallback sslsocketcontrol=%p\n", ssl.get())); if (ssl) { diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 9719cd66c42b..d16a7666aa96 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -83,6 +83,7 @@ extern PRThread *gSocketThread; #define INTL_ACCEPT_LANGUAGES "intl.accept_languages" #define BROWSER_PREF_PREFIX "browser.cache." #define DONOTTRACK_HEADER_ENABLED "privacy.donottrackheader.enabled" +#define H2MANDATORY_SUITE "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256" #define TELEMETRY_ENABLED "toolkit.telemetry.enabled" #define ALLOW_EXPERIMENTS "network.allow-experiments" #define SAFE_HINT_HEADER_VALUE "safeHint.enabled" @@ -146,6 +147,7 @@ nsHttpHandler::nsHttpHandler() , mMaxRequestAttempts(10) , mMaxRequestDelay(10) , mIdleSynTimeout(250) + , mH2MandatorySuiteEnabled(false) , mPipeliningEnabled(false) , mMaxConnections(24) , mMaxPersistentConnectionsPerServer(2) @@ -273,6 +275,7 @@ nsHttpHandler::Init() prefBranch->AddObserver(BROWSER_PREF("disk_cache_ssl"), this, true); prefBranch->AddObserver(DONOTTRACK_HEADER_ENABLED, this, true); prefBranch->AddObserver(TELEMETRY_ENABLED, this, true); + prefBranch->AddObserver(H2MANDATORY_SUITE, this, true); prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.short_lived_connections"), this, true); prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.long_lived_connections"), this, true); prefBranch->AddObserver(SAFE_HINT_HEADER_VALUE, this, true); @@ -1378,6 +1381,17 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) } } + // "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256" is the required h2 interop + // suite. + + if (PREF_CHANGED(H2MANDATORY_SUITE)) { + cVar = false; + rv = prefs->GetBoolPref(H2MANDATORY_SUITE, &cVar); + if (NS_SUCCEEDED(rv)) { + mH2MandatorySuiteEnabled = cVar; + } + } + // // network.allow-experiments // diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index 1414fd163df5..0905c3845cfc 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -318,6 +318,7 @@ public: PRIntervalTime GetPipelineTimeout() { return mPipelineReadTimeout; } SpdyInformation *SpdyInfo() { return &mSpdyInfo; } + bool IsH2MandatorySuiteEnabled() { return mH2MandatorySuiteEnabled; } // returns true in between Init and Shutdown states bool Active() { return mHandlerActive; } @@ -386,6 +387,7 @@ private: uint16_t mMaxRequestDelay; uint16_t mIdleSynTimeout; + bool mH2MandatorySuiteEnabled; bool mPipeliningEnabled; uint16_t mMaxConnections; uint8_t mMaxPersistentConnectionsPerServer; diff --git a/netwerk/test/unit/test_http2.js b/netwerk/test/unit/test_http2.js index 4c1d6f4df5a6..f282acce3013 100644 --- a/netwerk/test/unit/test_http2.js +++ b/netwerk/test/unit/test_http2.js @@ -533,6 +533,23 @@ function test_http2_pushapi_1() { chan.asyncOpen(listener, chan); } +var WrongSuiteListener = function() {}; +WrongSuiteListener.prototype = new Http2CheckListener(); +WrongSuiteListener.prototype.shouldBeHttp2 = false; +WrongSuiteListener.prototype.onStopRequest = function(request, ctx, status) { + prefs.setBoolPref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", true); + Http2CheckListener.prototype.onStopRequest.call(this); +}; + +// test that we use h1 without the mandatory cipher suite available +function test_http2_wrongsuite() { + prefs.setBoolPref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", false); + var chan = makeChan("https://localhost:6944/wrongsuite"); + chan.loadFlags = Ci.nsIRequest.LOAD_FRESH_CONNECTION | Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI; + var listener = new WrongSuiteListener(); + chan.asyncOpen(listener, null); +} + function test_http2_h11required_stream() { var chan = makeChan("https://localhost:6944/h11required_stream"); var listener = new Http2CheckListener(); @@ -601,6 +618,7 @@ var tests = [ test_http2_post_big , test_http2_h11required_stream , test_http2_h11required_session , test_http2_retry_rst + , test_http2_wrongsuite // cleanup , test_complete From e2c3dd796f545d38111f3c0a984abe31068cd501 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Tue, 13 Jan 2015 15:26:23 -0500 Subject: [PATCH 054/133] bug 1072478 - h2 push loses fin bit race condition 1/2 r=hurley --HG-- extra : rebase_source : 25ba5077d89f4cbf4c1a8e93a3c4da1536260d83 --- netwerk/protocol/http/Http2Push.cpp | 6 +++--- netwerk/protocol/http/SpdyPush31.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/netwerk/protocol/http/Http2Push.cpp b/netwerk/protocol/http/Http2Push.cpp index 864e89d165bc..7c05e39fef27 100644 --- a/netwerk/protocol/http/Http2Push.cpp +++ b/netwerk/protocol/http/Http2Push.cpp @@ -353,13 +353,13 @@ Http2PushTransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer, mIsDone = true; } - if (Available()) { + if (Available() || mIsDone) { Http2Stream *consumer = mPushStream->GetConsumerStream(); if (consumer) { LOG3(("Http2PushTransactionBuffer::WriteSegments notifying connection " - "consumer data available 0x%X [%u]\n", - mPushStream->StreamID(), Available())); + "consumer data available 0x%X [%u] done=%d\n", + mPushStream->StreamID(), Available(), mIsDone)); mPushStream->ConnectPushedStream(consumer); } } diff --git a/netwerk/protocol/http/SpdyPush31.cpp b/netwerk/protocol/http/SpdyPush31.cpp index 3084e7837f22..6c6869c31197 100644 --- a/netwerk/protocol/http/SpdyPush31.cpp +++ b/netwerk/protocol/http/SpdyPush31.cpp @@ -282,13 +282,13 @@ SpdyPush31TransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer, mIsDone = true; } - if (Available()) { + if (Available() || mIsDone) { SpdyStream31 *consumer = mPushStream->GetConsumerStream(); if (consumer) { LOG3(("SpdyPush31TransactionBuffer::WriteSegments notifying connection " - "consumer data available 0x%X [%u]\n", - mPushStream->StreamID(), Available())); + "consumer data available 0x%X [%u] done=%d\n", + mPushStream->StreamID(), Available(), mIsDone)); mPushStream->ConnectPushedStream(consumer); } } From d0d97ad7ef85f9a9147c9e22d2169c25076e81d7 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Tue, 13 Jan 2015 15:26:37 -0500 Subject: [PATCH 055/133] bug 1072478 - h2 push hit not subject to max_concurrent 2/2 r=hurley --HG-- extra : rebase_source : 4c49f74dc4b7ed8cead65dc3c78a2c8ae5ddf5e3 --- netwerk/protocol/http/Http2Push.cpp | 3 +- netwerk/protocol/http/Http2Session.cpp | 115 +++++++++++++++------ netwerk/protocol/http/Http2Session.h | 9 +- netwerk/protocol/http/Http2Stream.cpp | 83 +++++++++++---- netwerk/protocol/http/Http2Stream.h | 18 +++- netwerk/protocol/http/SpdyPush31.cpp | 3 +- netwerk/protocol/http/SpdySession31.cpp | 130 ++++++++++++++++-------- netwerk/protocol/http/SpdySession31.h | 7 +- netwerk/protocol/http/SpdyStream31.cpp | 71 ++++++++++--- netwerk/protocol/http/SpdyStream31.h | 22 +++- netwerk/protocol/http/nsHttpChannel.cpp | 1 + netwerk/test/unit/test_http2.js | 28 +++++ netwerk/test/unit/test_spdy.js | 28 +++++ testing/xpcshell/moz-http2/moz-http2.js | 23 +++++ testing/xpcshell/moz-spdy/moz-spdy.js | 25 +++++ 15 files changed, 444 insertions(+), 122 deletions(-) diff --git a/netwerk/protocol/http/Http2Push.cpp b/netwerk/protocol/http/Http2Push.cpp index 7c05e39fef27..e17eeefb728c 100644 --- a/netwerk/protocol/http/Http2Push.cpp +++ b/netwerk/protocol/http/Http2Push.cpp @@ -166,7 +166,8 @@ Http2PushedStream::ReadSegments(nsAHttpSegmentReader *, // the write side of a pushed transaction just involves manipulating a little state SetSentFin(true); - Http2Stream::mAllHeadersSent = 1; + Http2Stream::mRequestHeadersDone = 1; + Http2Stream::mOpenGenerated = 1; Http2Stream::ChangeState(UPSTREAM_COMPLETE); *count = 0; return NS_OK; diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index d60bdde62e99..74476e1c28dd 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -438,13 +438,15 @@ Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction, mStreamTransactionHash.Put(aHttpTransaction, stream); - if (RoomForMoreConcurrent()) { - LOG3(("Http2Session::AddStream %p stream %p activated immediately.", - this, stream)); - ActivateStream(stream); - } else { - LOG3(("Http2Session::AddStream %p stream %p queued.", this, stream)); - mQueuedStreams.Push(stream); + mReadyForWrite.Push(stream); + SetWriteCallbacks(); + + // Kick off the SYN transmit without waiting for the poll loop + // This won't work for the first stream because there is no segment reader + // yet. + if (mSegmentReader) { + uint32_t countRead; + ReadSegments(nullptr, kDefaultBufferSize, &countRead); } if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) && @@ -458,32 +460,26 @@ Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction, } void -Http2Session::ActivateStream(Http2Stream *stream) +Http2Session::QueueStream(Http2Stream *stream) { + // will be removed via processpending or a shutdown path MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), - "Do not activate pushed streams"); - MOZ_ASSERT(!stream->CountAsActive()); - stream->SetCountAsActive(true); - ++mConcurrent; + MOZ_ASSERT(!stream->Queued()); - if (mConcurrent > mConcurrentHighWater) - mConcurrentHighWater = mConcurrent; - LOG3(("Http2Session::AddStream %p activating stream %p Currently %d " - "streams in session, high water mark is %d", - this, stream, mConcurrent, mConcurrentHighWater)); + LOG3(("Http2Session::QueueStream %p stream %p queued.", this, stream)); - mReadyForWrite.Push(stream); - SetWriteCallbacks(); - - // Kick off the headers transmit without waiting for the poll loop - // This won't work for stream id=1 because there is no segment reader - // yet. - if (mSegmentReader) { - uint32_t countRead; - ReadSegments(nullptr, kDefaultBufferSize, &countRead); +#ifdef DEBUG + int32_t qsize = mQueuedStreams.GetSize(); + for (int32_t i = 0; i < qsize; i++) { + Http2Stream *qStream = static_cast(mQueuedStreams.ObjectAt(i)); + MOZ_ASSERT(qStream != stream); + MOZ_ASSERT(qStream->Queued()); } +#endif + + stream->SetQueued(true); + mQueuedStreams.Push(stream); } void @@ -491,13 +487,17 @@ Http2Session::ProcessPending() { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - while (RoomForMoreConcurrent()) { - Http2Stream *stream = static_cast(mQueuedStreams.PopFront()); - if (!stream) - return; - LOG3(("Http2Session::ProcessPending %p stream %p activated from queue.", + Http2Stream*stream; + while (RoomForMoreConcurrent() && + (stream = static_cast(mQueuedStreams.PopFront()))) { + + LOG3(("Http2Session::ProcessPending %p stream %p woken from queue.", this, stream)); - ActivateStream(stream); + MOZ_ASSERT(!stream->CountAsActive()); + MOZ_ASSERT(stream->Queued()); + stream->SetQueued(false); + mReadyForWrite.Push(stream); + SetWriteCallbacks(); } } @@ -616,6 +616,51 @@ Http2Session::ResetDownstreamState() mInputFrameDataStream = nullptr; } +// return true if activated (and counted against max) +// otherwise return false and queue +bool +Http2Session::TryToActivate(Http2Stream *aStream) +{ + if (aStream->Queued()) { + LOG3(("Http2Session::TryToActivate %p stream=%p already queued.\n", this, aStream)); + return false; + } + + if (!RoomForMoreConcurrent()) { + LOG3(("Http2Session::TryToActivate %p stream=%p no room for more concurrent " + "streams %d\n", this, aStream)); + QueueStream(aStream); + return false; + } + + LOG3(("Http2Session::TryToActivate %p stream=%p\n", this, aStream)); + IncrementConcurrent(aStream); + return true; +} + +void +Http2Session::IncrementConcurrent(Http2Stream *stream) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), + "Do not activate pushed streams"); + + nsAHttpTransaction *trans = stream->Transaction(); + if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) { + + MOZ_ASSERT(!stream->CountAsActive()); + stream->SetCountAsActive(true); + ++mConcurrent; + + if (mConcurrent > mConcurrentHighWater) { + mConcurrentHighWater = mConcurrent; + } + LOG3(("Http2Session::IncrementCounter %p counting stream %p Currently %d " + "streams in session, high water mark is %d\n", + this, stream, mConcurrent, mConcurrentHighWater)); + } +} + // call with data length (i.e. 0 for 0 data bytes - ignore 9 byte header) // dest must have 9 bytes of allocated space template void @@ -656,6 +701,7 @@ Http2Session::CreateFrameHeader(uint8_t *dest, uint16_t frameLength, void Http2Session::MaybeDecrementConcurrent(Http2Stream *aStream) { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG3(("MaybeDecrementConcurrent %p id=0x%X concurrent=%d active=%d\n", this, aStream->StreamID(), mConcurrent, aStream->CountAsActive())); @@ -1450,6 +1496,7 @@ Http2Session::RecvSettings(Http2Session *self) case SETTINGS_TYPE_MAX_CONCURRENT: self->mMaxConcurrent = value; Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value); + self->ProcessPending(); break; case SETTINGS_TYPE_INITIAL_WINDOW: @@ -1810,6 +1857,8 @@ Http2Session::RecvGoAway(Http2Session *self) for (uint32_t count = 0; count < size; ++count) { Http2Stream *stream = static_cast(self->mQueuedStreams.PopFront()); + MOZ_ASSERT(stream->Queued()); + stream->SetQueued(false); if (statusCode == HTTP_1_1_REQUIRED) { stream->Transaction()->DisableSpdy(); } diff --git a/netwerk/protocol/http/Http2Session.h b/netwerk/protocol/http/Http2Session.h index 1f3433cf12e7..655f0abe0a83 100644 --- a/netwerk/protocol/http/Http2Session.h +++ b/netwerk/protocol/http/Http2Session.h @@ -205,8 +205,8 @@ public: uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; } + bool TryToActivate(Http2Stream *stream); void ConnectPushedStream(Http2Stream *stream); - void MaybeDecrementConcurrent(Http2Stream *stream); nsresult ConfirmTLSProfile(); static bool ALPNCallback(nsISupports *securityInfo); @@ -266,8 +266,6 @@ private: void SetWriteCallbacks(); void RealignOutputQueue(); - bool RoomForMoreConcurrent(); - void ActivateStream(Http2Stream *); void ProcessPending(); nsresult SetInputFrameDataStream(uint32_t); void CreatePriorityNode(uint32_t, uint32_t, uint8_t, const char *); @@ -278,6 +276,11 @@ private: void UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes); void UpdateLocalSessionWindow(uint32_t bytes); + void MaybeDecrementConcurrent(Http2Stream *stream); + bool RoomForMoreConcurrent(); + void IncrementConcurrent(Http2Stream *stream); + void QueueStream(Http2Stream *stream); + // a wrapper for all calls to the nshttpconnection level segment writer. Used // to track network I/O for timeout purposes nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *); diff --git a/netwerk/protocol/http/Http2Stream.cpp b/netwerk/protocol/http/Http2Stream.cpp index 0de1dc866b87..84a262b48a63 100644 --- a/netwerk/protocol/http/Http2Stream.cpp +++ b/netwerk/protocol/http/Http2Stream.cpp @@ -46,8 +46,10 @@ Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction, , mSession(session) , mUpstreamState(GENERATING_HEADERS) , mState(IDLE) - , mAllHeadersSent(0) + , mRequestHeadersDone(0) + , mOpenGenerated(0) , mAllHeadersReceived(0) + , mQueued(0) , mTransaction(httpTransaction) , mSocketTransport(session->SocketTransport()) , mSegmentReader(nullptr) @@ -154,7 +156,7 @@ Http2Stream::ReadSegments(nsAHttpSegmentReader *reader, // If not, mark the stream for callback when writing can proceed. if (NS_SUCCEEDED(rv) && mUpstreamState == GENERATING_HEADERS && - !mAllHeadersSent) + !mRequestHeadersDone) mSession->TransactionHasDataToWrite(this); // mTxinlineFrameUsed represents any queued un-sent frame. It might @@ -169,10 +171,24 @@ Http2Stream::ReadSegments(nsAHttpSegmentReader *reader, if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed) mRequestBlockedOnRead = 1; + // A transaction that had already generated its headers before it was + // queued at the session level (due to concurrency concerns) may not call + // onReadSegment off the ReadSegments() stack above. + if (mUpstreamState == GENERATING_HEADERS && NS_SUCCEEDED(rv)) { + LOG3(("Http2Stream %p ReadSegments forcing OnReadSegment call\n", this)); + uint32_t wasted = 0; + mSegmentReader = reader; + OnReadSegment("", 0, &wasted); + mSegmentReader = nullptr; + } + // If the sending flow control window is open (!mBlockedOnRwin) then // continue sending the request - if (!mBlockedOnRwin && + if (!mBlockedOnRwin && mUpstreamState != GENERATING_HEADERS && !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) { + MOZ_ASSERT(!mQueued); + MOZ_ASSERT(mRequestHeadersDone); + MOZ_ASSERT(mOpenGenerated); LOG3(("Http2Stream::ReadSegments %p 0x%X: Sending request data complete, " "mUpstreamState=%x\n",this, mStreamID, mUpstreamState)); if (mSentFin) { @@ -303,16 +319,17 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, uint32_t *countUsed) { // Returns NS_OK even if the headers are incomplete - // set mAllHeadersSent flag if they are complete + // set mRequestHeadersDone flag if they are complete MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(mUpstreamState == GENERATING_HEADERS); + MOZ_ASSERT(!mRequestHeadersDone); LOG3(("Http2Stream::ParseHttpRequestHeaders %p avail=%d state=%x", this, avail, mUpstreamState)); mFlatHttpRequestHeaders.Append(buf, avail); - nsHttpRequestHead *head = mTransaction->RequestHead(); + const nsHttpRequestHead *head = mTransaction->RequestHead(); // We can use the simple double crlf because firefox is the // only client we are parsing @@ -333,7 +350,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, uint32_t oldLen = mFlatHttpRequestHeaders.Length(); mFlatHttpRequestHeaders.SetLength(endHeader + 2); *countUsed = avail - (oldLen - endHeader) + 4; - mAllHeadersSent = 1; + mRequestHeadersDone = 1; nsAutoCString authorityHeader; nsAutoCString hashkey; @@ -388,28 +405,32 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, SetSentFin(true); AdjustPushedPriority(); - // This stream has been activated (and thus counts against the concurrency - // limit intentionally), but will not be registered via - // RegisterStreamID (below) because of the push match. - // Release that semaphore count immediately (instead of waiting for - // cleanup stream) so we can initiate more pull streams. - mSession->MaybeDecrementConcurrent(this); - // There is probably pushed data buffered so trigger a read manually // as we can't rely on future network events to do it mSession->ConnectPushedStream(this); + mOpenGenerated = 1; return NS_OK; } } + return NS_OK; +} +// This is really a headers frame, but open is pretty clear from a workflow pov +nsresult +Http2Stream::GenerateOpen() +{ // It is now OK to assign a streamID that we are assured will // be monotonically increasing amongst new streams on this // session mStreamID = mSession->RegisterStreamID(this); MOZ_ASSERT(mStreamID & 1, "Http2 Stream Channel ID must be odd"); - LOG3(("Stream ID 0x%X [session=%p] for URI %s\n", - mStreamID, mSession, - nsCString(head->RequestURI()).get())); + MOZ_ASSERT(!mOpenGenerated); + + mOpenGenerated = 1; + + const nsHttpRequestHead *head = mTransaction->RequestHead(); + LOG3(("Http2Stream %p Stream ID 0x%X [session=%p] for URI %s\n", + this, mStreamID, mSession, nsCString(head->RequestURI()).get())); if (mStreamID >= 0x80000000) { // streamID must fit in 31 bits. Evading This is theoretically possible @@ -428,6 +449,9 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, // of HTTP/2 headers by writing to mTxInlineFrame{sz} nsCString compressedData; + nsAutoCString authorityHeader; + head->GetHeader(nsHttp::Host, authorityHeader); + nsDependentCString scheme(head->IsHTTPS() ? "https" : "http"); if (head->IsConnect()) { MOZ_ASSERT(mTransaction->QuerySpdyConnectTransaction()); @@ -440,6 +464,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, if (!ci) { return NS_ERROR_UNEXPECTED; } + authorityHeader = ci->GetHost(); authorityHeader.Append(':'); authorityHeader.AppendInt(ci->Port()); @@ -1200,12 +1225,26 @@ Http2Stream::OnReadSegment(const char *buf, // the number of those bytes that we consume (i.e. the portion that are // header bytes) - rv = ParseHttpRequestHeaders(buf, count, countRead); - if (NS_FAILED(rv)) - return rv; - LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d", - this, *countRead, count, mAllHeadersSent)); - if (mAllHeadersSent) { + if (!mRequestHeadersDone) { + if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) { + return rv; + } + } + + if (mRequestHeadersDone && !mOpenGenerated) { + if (!mSession->TryToActivate(this)) { + LOG3(("Http2Stream::OnReadSegment %p cannot activate now. queued.\n", this)); + return NS_OK; + } + if (NS_FAILED(rv = GenerateOpen())) { + return rv; + } + } + + LOG3(("ParseHttpRequestHeaders %p used %d of %d. " + "requestheadersdone = %d mOpenGenerated = %d\n", + this, *countRead, count, mRequestHeadersDone, mOpenGenerated)); + if (mOpenGenerated) { SetHTTPState(OPEN); AdjustInitialWindow(); // This version of TransmitFrame cannot block diff --git a/netwerk/protocol/http/Http2Stream.h b/netwerk/protocol/http/Http2Stream.h index 172fe59947b8..43510d584816 100644 --- a/netwerk/protocol/http/Http2Stream.h +++ b/netwerk/protocol/http/Http2Stream.h @@ -89,6 +89,9 @@ public: void SetSentReset(bool aStatus); bool SentReset() { return mSentReset; } + void SetQueued(bool aStatus) { mQueued = aStatus ? 1 : 0; } + bool Queued() { return mQueued; } + void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; } bool CountAsActive() { return mCountAsActive; } @@ -178,18 +181,27 @@ protected: // The HTTP/2 state for the stream from section 5.1 enum stateType mState; - // Flag is set when all http request headers have been read and ID is stable - uint32_t mAllHeadersSent : 1; + // Flag is set when all http request headers have been read ID is not stable + uint32_t mRequestHeadersDone : 1; - // Flag is set when all http request headers have been read and ID is stable + // Flag is set when ID is stable and concurrency limits are met + uint32_t mOpenGenerated : 1; + + // Flag is set when all http response headers have been read uint32_t mAllHeadersReceived : 1; + // Flag is set when stream is queued inside the session due to + // concurrency limits being exceeded + uint32_t mQueued : 1; + void ChangeState(enum upstreamStateType); private: friend class nsAutoPtr; nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *); + nsresult GenerateOpen(); + void AdjustPushedPriority(); void AdjustInitialWindow(); nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment); diff --git a/netwerk/protocol/http/SpdyPush31.cpp b/netwerk/protocol/http/SpdyPush31.cpp index 6c6869c31197..8fb7d795b594 100644 --- a/netwerk/protocol/http/SpdyPush31.cpp +++ b/netwerk/protocol/http/SpdyPush31.cpp @@ -106,7 +106,8 @@ SpdyPushedStream31::ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *co // the write side of a pushed transaction just involves manipulating a little state SpdyStream31::mSentFinOnData = 1; - SpdyStream31::mSynFrameComplete = 1; + SpdyStream31::mRequestHeadersDone = 1; + SpdyStream31::mSynFrameGenerated = 1; SpdyStream31::ChangeState(UPSTREAM_COMPLETE); *count = 0; return NS_OK; diff --git a/netwerk/protocol/http/SpdySession31.cpp b/netwerk/protocol/http/SpdySession31.cpp index 0647e02723b8..a6bc9cff54ff 100644 --- a/netwerk/protocol/http/SpdySession31.cpp +++ b/netwerk/protocol/http/SpdySession31.cpp @@ -379,14 +379,15 @@ SpdySession31::AddStream(nsAHttpTransaction *aHttpTransaction, mStreamTransactionHash.Put(aHttpTransaction, stream); - if (RoomForMoreConcurrent()) { - LOG3(("SpdySession31::AddStream %p stream %p activated immediately.", - this, stream)); - ActivateStream(stream); - } - else { - LOG3(("SpdySession31::AddStream %p stream %p queued.", this, stream)); - mQueuedStreams.Push(stream); + mReadyForWrite.Push(stream); + SetWriteCallbacks(); + + // Kick off the SYN transmit without waiting for the poll loop + // This won't work for stream id=1 because there is no segment reader + // yet. + if (mSegmentReader) { + uint32_t countRead; + ReadSegments(nullptr, kDefaultBufferSize, &countRead); } if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) && @@ -400,33 +401,26 @@ SpdySession31::AddStream(nsAHttpTransaction *aHttpTransaction, } void -SpdySession31::ActivateStream(SpdyStream31 *stream) +SpdySession31::QueueStream(SpdyStream31 *stream) { + // will be removed via processpending or a shutdown path MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), - "Do not activate pushed streams"); + MOZ_ASSERT(!stream->CountAsActive()); + MOZ_ASSERT(!stream->Queued()); - nsAHttpTransaction *trans = stream->Transaction(); - if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) { - ++mConcurrent; - if (mConcurrent > mConcurrentHighWater) { - mConcurrentHighWater = mConcurrent; - } - LOG3(("SpdySession31::AddStream %p activating stream %p Currently %d " - "streams in session, high water mark is %d", - this, stream, mConcurrent, mConcurrentHighWater)); + LOG3(("SpdySession31::QueueStream %p stream %p queued.", this, stream)); + +#ifdef DEBUG + int32_t qsize = mQueuedStreams.GetSize(); + for (int32_t i = 0; i < qsize; i++) { + SpdyStream31 *qStream = static_cast(mQueuedStreams.ObjectAt(i)); + MOZ_ASSERT(qStream != stream); + MOZ_ASSERT(qStream->Queued()); } +#endif - mReadyForWrite.Push(stream); - SetWriteCallbacks(); - - // Kick off the SYN transmit without waiting for the poll loop - // This won't work for stream id=1 because there is no segment reader - // yet. - if (mSegmentReader) { - uint32_t countRead; - ReadSegments(nullptr, kDefaultBufferSize, &countRead); - } + stream->SetQueued(true); + mQueuedStreams.Push(stream); } void @@ -434,13 +428,17 @@ SpdySession31::ProcessPending() { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - while (RoomForMoreConcurrent()) { - SpdyStream31 *stream = static_cast(mQueuedStreams.PopFront()); - if (!stream) - return; - LOG3(("SpdySession31::ProcessPending %p stream %p activated from queue.", + SpdyStream31 *stream; + while (RoomForMoreConcurrent() && + (stream = static_cast(mQueuedStreams.PopFront()))) { + + LOG3(("SpdySession31::ProcessPending %p stream %p woken from queue.", this, stream)); - ActivateStream(stream); + MOZ_ASSERT(!stream->CountAsActive()); + MOZ_ASSERT(stream->Queued()); + stream->SetQueued(false); + mReadyForWrite.Push(stream); + SetWriteCallbacks(); } } @@ -561,18 +559,67 @@ SpdySession31::ResetDownstreamState() mInputFrameDataStream = nullptr; } +// return true if activated (and counted against max) +// otherwise return false and queue +bool +SpdySession31::TryToActivate(SpdyStream31 *aStream) +{ + if (aStream->Queued()) { + LOG3(("SpdySession31::TryToActivate %p stream=%p already queued.\n", this, aStream)); + return false; + } + + if (!RoomForMoreConcurrent()) { + LOG3(("SpdySession31::TryToActivate %p stream=%p no room for more concurrent " + "streams %d\n", this, aStream)); + QueueStream(aStream); + return false; + } + + LOG3(("SpdySession31::TryToActivate %p stream=%p\n", this, aStream)); + IncrementConcurrent(aStream); + return true; +} + +void +SpdySession31::IncrementConcurrent(SpdyStream31 *stream) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), + "Do not activate pushed streams"); + + nsAHttpTransaction *trans = stream->Transaction(); + if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) { + + MOZ_ASSERT(!stream->CountAsActive()); + stream->SetCountAsActive(true); + ++mConcurrent; + + if (mConcurrent > mConcurrentHighWater) { + mConcurrentHighWater = mConcurrent; + } + LOG3(("SpdySession31::AddStream %p counting stream %p Currently %d " + "streams in session, high water mark is %d", + this, stream, mConcurrent, mConcurrentHighWater)); + } +} + void SpdySession31::DecrementConcurrent(SpdyStream31 *aStream) { - uint32_t id = aStream->StreamID(); + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - if (id && !(id & 0x1)) - return; // pushed streams aren't counted in concurrent limit + if (!aStream->CountAsActive()) { + return; + } MOZ_ASSERT(mConcurrent); + aStream->SetCountAsActive(false); --mConcurrent; + LOG3(("DecrementConcurrent %p id=0x%X concurrent=%d\n", - this, id, mConcurrent)); + this, aStream->StreamID(), mConcurrent)); + ProcessPending(); } @@ -1414,6 +1461,7 @@ SpdySession31::HandleSettings(SpdySession31 *self) case SETTINGS_TYPE_MAX_CONCURRENT: self->mMaxConcurrent = value; Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value); + self->ProcessPending(); break; case SETTINGS_TYPE_CWND: @@ -1538,6 +1586,8 @@ SpdySession31::HandleGoAway(SpdySession31 *self) for (uint32_t count = 0; count < size; ++count) { SpdyStream31 *stream = static_cast(self->mQueuedStreams.PopFront()); + MOZ_ASSERT(stream->Queued()); + stream->SetQueued(false); self->CloseStream(stream, NS_ERROR_NET_RESET); self->mStreamTransactionHash.Remove(stream->Transaction()); } diff --git a/netwerk/protocol/http/SpdySession31.h b/netwerk/protocol/http/SpdySession31.h index 6192ca019881..595b06f4b96c 100644 --- a/netwerk/protocol/http/SpdySession31.h +++ b/netwerk/protocol/http/SpdySession31.h @@ -178,6 +178,7 @@ public: uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; } + bool TryToActivate(SpdyStream31 *stream); void ConnectPushedStream(SpdyStream31 *stream); void DecrementConcurrent(SpdyStream31 *stream); @@ -223,8 +224,6 @@ private: void SetWriteCallbacks(); void RealignOutputQueue(); - bool RoomForMoreConcurrent(); - void ActivateStream(SpdyStream31 *); void ProcessPending(); nsresult SetInputFrameDataStream(uint32_t); bool VerifyStream(SpdyStream31 *, uint32_t); @@ -234,6 +233,10 @@ private: void UpdateLocalStreamWindow(SpdyStream31 *stream, uint32_t bytes); void UpdateLocalSessionWindow(uint32_t bytes); + bool RoomForMoreConcurrent(); + void IncrementConcurrent(SpdyStream31 *stream); + void QueueStream(SpdyStream31 *stream); + // a wrapper for all calls to the nshttpconnection level segment writer. Used // to track network I/O for timeout purposes nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *); diff --git a/netwerk/protocol/http/SpdyStream31.cpp b/netwerk/protocol/http/SpdyStream31.cpp index 068720575ae6..67a1db5d216e 100644 --- a/netwerk/protocol/http/SpdyStream31.cpp +++ b/netwerk/protocol/http/SpdyStream31.cpp @@ -42,8 +42,10 @@ SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction, : mStreamID(0) , mSession(spdySession) , mUpstreamState(GENERATING_SYN_STREAM) - , mSynFrameComplete(0) + , mRequestHeadersDone(0) + , mSynFrameGenerated(0) , mSentFinOnData(0) + , mQueued(0) , mTransaction(httpTransaction) , mSocketTransport(spdySession->SocketTransport()) , mSegmentReader(nullptr) @@ -55,6 +57,7 @@ SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction, , mSentWaitingFor(0) , mReceivedData(0) , mSetTCPSocketBuffer(0) + , mCountAsActive(0) , mTxInlineFrameSize(SpdySession31::kDefaultBufferSize) , mTxInlineFrameUsed(0) , mTxStreamFrameSize(0) @@ -129,7 +132,7 @@ SpdyStream31::ReadSegments(nsAHttpSegmentReader *reader, // If not, mark the stream for callback when writing can proceed. if (NS_SUCCEEDED(rv) && mUpstreamState == GENERATING_SYN_STREAM && - !mSynFrameComplete) + !mRequestHeadersDone) mSession->TransactionHasDataToWrite(this); // mTxinlineFrameUsed represents any queued un-sent frame. It might @@ -144,17 +147,27 @@ SpdyStream31::ReadSegments(nsAHttpSegmentReader *reader, if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed) mRequestBlockedOnRead = 1; + // A transaction that had already generated its headers before it was + // queued at the session level (due to concurrency concerns) may not call + // onReadSegment off the ReadSegments() stack above. + if (mUpstreamState == GENERATING_SYN_STREAM && NS_SUCCEEDED(rv)) { + LOG3(("SpdyStream31 %p ReadSegments forcing OnReadSegment call\n", this)); + uint32_t wasted = 0; + mSegmentReader = reader; + OnReadSegment("", 0, &wasted); + mSegmentReader = nullptr; + } + // If the sending flow control window is open (!mBlockedOnRwin) then // continue sending the request - if (!mBlockedOnRwin && + if (!mBlockedOnRwin && mUpstreamState != GENERATING_SYN_STREAM && !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) { LOG3(("SpdyStream31::ReadSegments %p 0x%X: Sending request data complete, " "mUpstreamState=%x finondata=%d",this, mStreamID, mUpstreamState, mSentFinOnData)); if (mSentFinOnData) { ChangeState(UPSTREAM_COMPLETE); - } - else { + } else { GenerateDataFrameHeader(0, true); ChangeState(SENDING_FIN_STREAM); mSession->TransactionHasDataToWrite(this); @@ -259,10 +272,11 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf, uint32_t *countUsed) { // Returns NS_OK even if the headers are incomplete - // set mSynFrameComplete flag if they are complete + // set mRequestHeadersDone flag if they are complete MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(mUpstreamState == GENERATING_SYN_STREAM); + MOZ_ASSERT(!mRequestHeadersDone); LOG3(("SpdyStream31::ParseHttpRequestHeaders %p avail=%d state=%x", this, avail, mUpstreamState)); @@ -288,7 +302,7 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf, uint32_t oldLen = mFlatHttpRequestHeaders.Length(); mFlatHttpRequestHeaders.SetLength(endHeader + 2); *countUsed = avail - (oldLen - endHeader) + 4; - mSynFrameComplete = 1; + mRequestHeadersDone = 1; nsAutoCString hostHeader; nsAutoCString hashkey; @@ -330,15 +344,24 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf, // There is probably pushed data buffered so trigger a read manually // as we can't rely on future network events to do it mSession->ConnectPushedStream(this); + mSynFrameGenerated = 1; return NS_OK; } } + return NS_OK; +} +nsresult +SpdyStream31::GenerateSynFrame() +{ // It is now OK to assign a streamID that we are assured will // be monotonically increasing amongst syn-streams on this // session mStreamID = mSession->RegisterStreamID(this); MOZ_ASSERT(mStreamID & 1, "Spdy Stream Channel ID must be odd"); + MOZ_ASSERT(!mSynFrameGenerated); + + mSynFrameGenerated = 1; if (mStreamID >= 0x80000000) { // streamID must fit in 31 bits. This is theoretically possible @@ -507,6 +530,8 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf, CompressToFrame(NS_LITERAL_CSTRING(":version")); CompressToFrame(versionHeader); + nsAutoCString hostHeader; + mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader); CompressToFrame(NS_LITERAL_CSTRING(":host")); CompressToFrame(hostHeader); @@ -821,13 +846,15 @@ SpdyStream31::ChangeState(enum stateType newState) void SpdyStream31::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame) { - LOG3(("SpdyStream31::GenerateDataFrameHeader %p len=%d last=%d", - this, dataLength, lastFrame)); + LOG3(("SpdyStream31::GenerateDataFrameHeader %p len=%d last=%d id=0x%X\n", + this, dataLength, lastFrame, mStreamID)); MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(!mTxInlineFrameUsed, "inline frame not empty"); MOZ_ASSERT(!mTxStreamFrameSize, "stream frame not empty"); MOZ_ASSERT(!(dataLength & 0xff000000), "datalength > 24 bits"); + MOZ_ASSERT(mStreamID != 0); + MOZ_ASSERT(mStreamID != SpdySession31::kDeadStreamID); (reinterpret_cast(mTxInlineFrame.get()))[0] = PR_htonl(mStreamID); (reinterpret_cast(mTxInlineFrame.get()))[1] = @@ -1455,12 +1482,26 @@ SpdyStream31::OnReadSegment(const char *buf, // the number of those bytes that we consume (i.e. the portion that are // header bytes) - rv = ParseHttpRequestHeaders(buf, count, countRead); - if (NS_FAILED(rv)) - return rv; - LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d", - this, *countRead, count, mSynFrameComplete)); - if (mSynFrameComplete) { + if (!mRequestHeadersDone) { + if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) { + return rv; + } + } + + if (mRequestHeadersDone && !mSynFrameGenerated) { + if (!mSession->TryToActivate(this)) { + LOG3(("SpdyStream31::OnReadSegment %p cannot activate now. queued.\n", this)); + return NS_OK; + } + if (NS_FAILED(rv = GenerateSynFrame())) { + return rv; + } + } + + LOG3(("ParseHttpRequestHeaders %p used %d of %d. " + "requestheadersdone = %d mSynFrameGenerated = %d\n", + this, *countRead, count, mRequestHeadersDone, mSynFrameGenerated)); + if (mSynFrameGenerated) { AdjustInitialWindow(); rv = TransmitFrame(nullptr, nullptr, true); if (rv == NS_BASE_STREAM_WOULD_BLOCK) { diff --git a/netwerk/protocol/http/SpdyStream31.h b/netwerk/protocol/http/SpdyStream31.h index 77d12c8b2222..b1fe1407bbbc 100644 --- a/netwerk/protocol/http/SpdyStream31.h +++ b/netwerk/protocol/http/SpdyStream31.h @@ -55,6 +55,12 @@ public: void SetRecvdData(bool aStatus) { mReceivedData = aStatus ? 1 : 0; } bool RecvdData() { return mReceivedData; } + void SetQueued(bool aStatus) { mQueued = aStatus ? 1 : 0; } + bool Queued() { return mQueued; } + + void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; } + bool CountAsActive() { return mCountAsActive; } + void UpdateTransportSendEvents(uint32_t count); void UpdateTransportReadEvents(uint32_t count); @@ -118,13 +124,20 @@ protected: // sending_request_body for each SPDY chunk in the upload. enum stateType mUpstreamState; - // Flag is set when all http request headers have been read and ID is stable - uint32_t mSynFrameComplete : 1; + // Flag is set when all http request headers have been read + uint32_t mRequestHeadersDone : 1; + + // Flag is set when stream ID is stable + uint32_t mSynFrameGenerated : 1; // Flag is set when a FIN has been placed on a data or syn packet // (i.e after the client has closed) uint32_t mSentFinOnData : 1; + // Flag is set when stream is queued inside the session due to + // concurrency limits being exceeded + uint32_t mQueued : 1; + void ChangeState(enum stateType); private: @@ -135,6 +148,8 @@ private: void *); nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *); + nsresult GenerateSynFrame(); + void AdjustInitialWindow(); nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment); void GenerateDataFrameHeader(uint32_t, bool); @@ -185,6 +200,9 @@ private: // Flag is set after TCP send autotuning has been disabled uint32_t mSetTCPSocketBuffer : 1; + // Flag is set when stream is counted towards MAX_CONCURRENT streams in session + uint32_t mCountAsActive : 1; + // The InlineFrame and associated data is used for composing control // frames and data frame headers. nsAutoArrayPtr mTxInlineFrame; diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 0ce594038da8..29fe2324fb9c 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -347,6 +347,7 @@ nsHttpChannel::Connect() // do not continue if asyncOpenCacheEntry is in progress if (AwaitingCacheCallbacks()) { + LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n", this)); MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state"); return NS_OK; } diff --git a/netwerk/test/unit/test_http2.js b/netwerk/test/unit/test_http2.js index f282acce3013..ff5e46b2966b 100644 --- a/netwerk/test/unit/test_http2.js +++ b/netwerk/test/unit/test_http2.js @@ -271,6 +271,33 @@ function test_http2_xhr() { req.send(null); } +var concurrent_channels = []; + +var Http2ConcurrentListener = function() {}; + +Http2ConcurrentListener.prototype = new Http2CheckListener(); +Http2ConcurrentListener.prototype.count = 0; +Http2ConcurrentListener.prototype.target = 0; + +Http2ConcurrentListener.prototype.onStopRequest = function(request, ctx, status) { + this.count++; + do_check_true(this.isHttp2Connection); + if (this.count == this.target) { + run_next_test(); + do_test_finished(); + } +}; + +function test_http2_concurrent() { + var concurrent_listener = new Http2ConcurrentListener(); + concurrent_listener.target = 201; + for (var i = 0; i < concurrent_listener.target; i++) { + concurrent_channels[i] = makeChan("https://localhost:6944/750ms"); + concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE; + concurrent_channels[i].asyncOpen(concurrent_listener, null); + } +} + // Test to make sure we get multiplexing right function test_http2_multiplex() { var chan1 = makeChan("https://localhost:6944/multiplex1"); @@ -598,6 +625,7 @@ function test_complete() { // a stalled stream when a SETTINGS frame arrives var tests = [ test_http2_post_big , test_http2_basic + , test_http2_concurrent , test_http2_basic_unblocked_dep , test_http2_nospdy , test_http2_push1 diff --git a/netwerk/test/unit/test_spdy.js b/netwerk/test/unit/test_spdy.js index 9c333cd558ec..39f96268e868 100644 --- a/netwerk/test/unit/test_spdy.js +++ b/netwerk/test/unit/test_spdy.js @@ -231,6 +231,33 @@ function test_spdy_xhr() { req.send(null); } +var concurrent_channels = []; + +var SpdyConcurrentListener = function() {}; + +SpdyConcurrentListener.prototype = new SpdyCheckListener(); +SpdyConcurrentListener.prototype.count = 0; +SpdyConcurrentListener.prototype.target = 0; + +SpdyConcurrentListener.prototype.onStopRequest = function(request, ctx, status) { + this.count++; + do_check_true(this.isSpdyConnection); + if (this.count == this.target) { + run_next_test(); + do_test_finished(); + } +}; + +function test_spdy_concurrent() { + var concurrent_listener = new SpdyConcurrentListener(); + concurrent_listener.target = 201; + for (var i = 0; i < concurrent_listener.target; i++) { + concurrent_channels[i] = makeChan("https://localhost:4443/750ms"); + concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE; + concurrent_channels[i].asyncOpen(concurrent_listener, null); + } +} + // Test to make sure we get multiplexing right function test_spdy_multiplex() { var chan1 = makeChan("https://localhost:4443/multiplex1"); @@ -320,6 +347,7 @@ function test_spdy_post_big() { // a stalled stream when a SETTINGS frame arrives var tests = [ test_spdy_post_big , test_spdy_basic + , test_spdy_concurrent , test_spdy_push1 , test_spdy_push2 , test_spdy_push3 diff --git a/testing/xpcshell/moz-http2/moz-http2.js b/testing/xpcshell/moz-http2/moz-http2.js index 6c8ed2d359c1..8c4b5451d05c 100644 --- a/testing/xpcshell/moz-http2/moz-http2.js +++ b/testing/xpcshell/moz-http2/moz-http2.js @@ -86,6 +86,21 @@ var m = { } }; +var runlater = function() {}; +runlater.prototype = { + req : null, + resp : null, + + onTimeout : function onTimeout() { + this.resp.writeHead(200); + this.resp.end("It's all good 750ms."); + } +}; + +function executeRunLater(arg) { + arg.onTimeout(); +} + var h11required_conn = null; var h11required_header = "yes"; var didRst = false; @@ -111,6 +126,14 @@ function handleRequest(req, res) { process.exit(); } + if (u.pathname === '/750ms') { + var rl = new runlater(); + rl.req = req; + rl.resp = res; + setTimeout(executeRunLater, 750, rl); + return; + } + else if ((u.pathname === '/multiplex1') && (req.httpVersionMajor === 2)) { res.setHeader('Content-Type', 'text/plain'); res.writeHead(200); diff --git a/testing/xpcshell/moz-spdy/moz-spdy.js b/testing/xpcshell/moz-spdy/moz-spdy.js index dcf87e7104ed..20c7e819a00f 100644 --- a/testing/xpcshell/moz-spdy/moz-spdy.js +++ b/testing/xpcshell/moz-spdy/moz-spdy.js @@ -74,6 +74,21 @@ function finishPost(res, content) { res.end(content); } +var runlater = function() {}; +runlater.prototype = { + req : null, + resp : null, + + onTimeout : function onTimeout() { + this.resp.writeHead(200); + this.resp.end("It's all good spdy 750ms."); + } +}; + +function executeRunLater(arg) { + arg.onTimeout(); +} + function handleRequest(req, res) { var u = url.parse(req.url); var content = getHttpContent(u.pathname); @@ -85,6 +100,16 @@ function handleRequest(req, res) { res.setHeader('X-Connection-Spdy', 'no'); } +console.log(u.pathname); + + if (u.pathname === '/750ms') { + var rl = new runlater(); + rl.req = req; + rl.resp = res; + setTimeout(executeRunLater, 750, rl); + return; + } + if (u.pathname == '/exit') { res.setHeader('Content-Type', 'text/plain'); res.writeHead(200); From 5c867be0e8019d43f0ddfd3d9216c0f5fad67ee2 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 16 Jan 2015 09:56:30 -0800 Subject: [PATCH 056/133] Bug 1119753 - Fix CPOW GC crash (r=jonco) --- js/ipc/JavaScriptParent.cpp | 8 +++----- js/ipc/JavaScriptShared.cpp | 23 +++++++++++++++++++++++ js/ipc/JavaScriptShared.h | 6 +++++- js/ipc/WrapperOwner.cpp | 4 ++++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/js/ipc/JavaScriptParent.cpp b/js/ipc/JavaScriptParent.cpp index 6c206e57720a..3411c537bc5d 100644 --- a/js/ipc/JavaScriptParent.cpp +++ b/js/ipc/JavaScriptParent.cpp @@ -51,11 +51,9 @@ JavaScriptParent::init() void JavaScriptParent::trace(JSTracer *trc) { - if (active()) { - objects_.trace(trc); - unwaivedObjectIds_.trace(trc); - waivedObjectIds_.trace(trc); - } + objects_.trace(trc); + unwaivedObjectIds_.trace(trc); + waivedObjectIds_.trace(trc); } JSObject * diff --git a/js/ipc/JavaScriptShared.cpp b/js/ipc/JavaScriptShared.cpp index 6c0179a69822..c5537e05af86 100644 --- a/js/ipc/JavaScriptShared.cpp +++ b/js/ipc/JavaScriptShared.cpp @@ -73,6 +73,18 @@ IdToObjectMap::remove(ObjectId id) table_.remove(id); } +void +IdToObjectMap::clear() +{ + table_.clear(); +} + +bool +IdToObjectMap::empty() const +{ + return table_.empty(); +} + ObjectToIdMap::ObjectToIdMap() : table_(nullptr) { @@ -157,6 +169,12 @@ ObjectToIdMap::remove(JSObject *obj) table_->remove(obj); } +void +ObjectToIdMap::clear() +{ + table_->clear(); +} + bool JavaScriptShared::sLoggingInitialized; bool JavaScriptShared::sLoggingEnabled; bool JavaScriptShared::sStackLoggingEnabled; @@ -181,6 +199,11 @@ JavaScriptShared::JavaScriptShared(JSRuntime *rt) } } +JavaScriptShared::~JavaScriptShared() +{ + MOZ_RELEASE_ASSERT(cpows_.empty()); +} + bool JavaScriptShared::init() { diff --git a/js/ipc/JavaScriptShared.h b/js/ipc/JavaScriptShared.h index 6d0ea40d28b3..fb381680b942 100644 --- a/js/ipc/JavaScriptShared.h +++ b/js/ipc/JavaScriptShared.h @@ -110,6 +110,9 @@ class IdToObjectMap JSObject *find(ObjectId id); void remove(ObjectId id); + void clear(); + bool empty() const; + private: Table table_; }; @@ -131,6 +134,7 @@ class ObjectToIdMap bool add(JSContext *cx, JSObject *obj, ObjectId id); ObjectId find(JSObject *obj); void remove(JSObject *obj); + void clear(); private: static void keyMarkCallback(JSTracer *trc, JSObject *key, void *data); @@ -144,7 +148,7 @@ class JavaScriptShared { public: explicit JavaScriptShared(JSRuntime *rt); - virtual ~JavaScriptShared() {} + virtual ~JavaScriptShared(); bool init(); diff --git a/js/ipc/WrapperOwner.cpp b/js/ipc/WrapperOwner.cpp index 4b04532250f3..d306d1652776 100644 --- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -877,6 +877,10 @@ void WrapperOwner::ActorDestroy(ActorDestroyReason why) { inactive_ = true; + + objects_.clear(); + unwaivedObjectIds_.clear(); + waivedObjectIds_.clear(); } bool From 3337f70861288a0f37dd61329578e2e864030adc Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 16 Jan 2015 09:57:33 -0800 Subject: [PATCH 057/133] Bug 1122303 - nsIProcess::Run needs to handle EINTR on Mac (r=nfroyd) --- xpcom/threads/nsProcessCommon.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xpcom/threads/nsProcessCommon.cpp b/xpcom/threads/nsProcessCommon.cpp index 4880f478e109..0ce2e1eb282d 100644 --- a/xpcom/threads/nsProcessCommon.cpp +++ b/xpcom/threads/nsProcessCommon.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #endif #include #include @@ -264,7 +265,11 @@ nsProcess::Monitor(void* aArg) #ifdef XP_MACOSX int exitCode = -1; int status = 0; - if (waitpid(process->mPid, &status, 0) == process->mPid) { + pid_t result; + do { + result = waitpid(process->mPid, &status, 0); + } while (result == -1 && errno == EINTR); + if (result == process->mPid) { if (WIFEXITED(status)) { exitCode = WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { From 1ed4ec44922a85255736cd80c95b9c40d388c05f Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 16 Jan 2015 10:06:11 -0800 Subject: [PATCH 058/133] Bug 1121713 - [e10s] CompositorParent should clear itself out of LayerTreeState when destroyed (r=nical) --- gfx/layers/ipc/CompositorParent.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index e9b05d19c2b1..0ab140ff404e 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -481,6 +481,7 @@ CompositorParent::RecvWillStop() if (lts->mParent == this) { mLayerManager->ClearCachedResources(lts->mRoot); lts->mLayerManager = nullptr; + lts->mParent = nullptr; } } mLayerManager->Destroy(); @@ -1709,7 +1710,9 @@ CrossProcessCompositorParent::ForceComposite(LayerTransactionParent* aLayerTree) MonitorAutoLock lock(*sIndirectLayerTreesLock); parent = sIndirectLayerTrees[id].mParent; } - parent->ForceComposite(aLayerTree); + if (parent) { + parent->ForceComposite(aLayerTree); + } } bool From babe73ca7407932edd28f9cf850260f9925ca500 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 16 Jan 2015 10:06:56 -0800 Subject: [PATCH 059/133] Bug 567058 - Refactor b2g ProvideWindow to prepare for desktop changes (r=bent) --- dom/ipc/TabChild.cpp | 32 ++++++++++++++++++++++---------- dom/ipc/TabChild.h | 16 ++++++++++------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index c7245f67d76f..78ca541baa54 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1437,8 +1437,16 @@ TabChild::ProvideWindow(nsIDOMWindow* aParent, uint32_t aChromeFlags, // Note that BrowserFrameProvideWindow may return NS_ERROR_ABORT if the // open window call was canceled. It's important that we pass this error // code back to our caller. - return BrowserFrameProvideWindow(aParent, aURI, aName, aFeatures, - aWindowIsNew, aReturn); + return ProvideWindowCommon(aParent, + aChromeFlags, + aCalledFromJS, + aPositionSpecified, + aSizeSpecified, + aURI, + aName, + aFeatures, + aWindowIsNew, + aReturn); } int32_t openLocation = @@ -1510,12 +1518,16 @@ TabChild::ProvideWindow(nsIDOMWindow* aParent, uint32_t aChromeFlags, } nsresult -TabChild::BrowserFrameProvideWindow(nsIDOMWindow* aOpener, - nsIURI* aURI, - const nsAString& aName, - const nsACString& aFeatures, - bool* aWindowIsNew, - nsIDOMWindow** aReturn) +TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, + bool aSizeSpecified, + nsIURI* aURI, + const nsAString& aName, + const nsACString& aFeatures, + bool* aWindowIsNew, + nsIDOMWindow** aReturn) { *aReturn = nullptr; @@ -1539,7 +1551,7 @@ TabChild::BrowserFrameProvideWindow(nsIDOMWindow* aOpener, &tabId); nsRefPtr newChild = new TabChild(ContentChild::GetSingleton(), tabId, - /* TabContext */ *this, /* chromeFlags */ 0); + /* TabContext */ *this, aChromeFlags); if (NS_FAILED(newChild->Init())) { return NS_ERROR_ABORT; } @@ -1548,7 +1560,7 @@ TabChild::BrowserFrameProvideWindow(nsIDOMWindow* aOpener, unused << Manager()->SendPBrowserConstructor( // We release this ref in DeallocPBrowserChild nsRefPtr(newChild).forget().take(), - tabId, IPCTabContext(context, mScrolling), /* chromeFlags */ 0, + tabId, IPCTabContext(context, mScrolling), aChromeFlags, cc->GetID(), cc->IsForApp(), cc->IsForBrowser()); nsAutoCString spec; diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index c6abbd581b28..6b8ca5254c33 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -574,12 +574,16 @@ private: void UpdateTapState(const WidgetTouchEvent& aEvent, nsEventStatus aStatus); nsresult - BrowserFrameProvideWindow(nsIDOMWindow* aOpener, - nsIURI* aURI, - const nsAString& aName, - const nsACString& aFeatures, - bool* aWindowIsNew, - nsIDOMWindow** aReturn); + ProvideWindowCommon(nsIDOMWindow* aOpener, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, + bool aSizeSpecified, + nsIURI* aURI, + const nsAString& aName, + const nsACString& aFeatures, + bool* aWindowIsNew, + nsIDOMWindow** aReturn); bool HasValidInnerSize(); From 9abd6a3719d18cc48a74fbbe481186dc717771f3 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 16 Jan 2015 10:07:50 -0800 Subject: [PATCH 060/133] Bug 567058 - Stop using intr messages for window.open (r=bent) --- dom/base/nsFrameLoader.cpp | 2 +- dom/ipc/ContentParent.cpp | 5 ++ dom/ipc/PBrowser.ipdl | 11 ++- dom/ipc/TabChild.cpp | 178 +++++++++++++++++++------------------ dom/ipc/TabChild.h | 1 + dom/ipc/TabParent.cpp | 105 +++++++++++++++++----- dom/ipc/TabParent.h | 55 +++++++++--- 7 files changed, 237 insertions(+), 120 deletions(-) diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index ef2623e9d64f..d782137e5128 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -2304,7 +2304,7 @@ nsFrameLoader::CreateStaticClone(nsIFrameLoader* aDest) bool nsFrameLoader::DoLoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope) { - mozilla::dom::PBrowserParent* tabParent = GetRemoteBrowser(); + auto* tabParent = static_cast(GetRemoteBrowser()); if (tabParent) { return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 0c22b7e4deb5..934b27553996 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -994,6 +994,11 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext, return nullptr; } + if (TabParent* parent = TabParent::GetNextTabParent()) { + parent->SetOwnerElement(aFrameElement); + return parent; + } + ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement); bool isInContentProcess = (XRE_GetProcessType() != GeckoProcessType_Default); TabId tabId; diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index adebdee343e8..d20da8dc3436 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -79,6 +79,12 @@ struct ShowInfo double defaultScale; }; +struct FrameScriptInfo +{ + nsString url; + bool runInGlobalScope; +}; + prio(normal upto urgent) intr protocol PBrowser { manager PContent or PContentBridge; @@ -117,7 +123,8 @@ parent: Event(RemoteDOMEvent aEvent); - intr CreateWindow(uint32_t aChromeFlags, + sync CreateWindow(PBrowser aNewTab, + uint32_t aChromeFlags, bool aCalledFromJS, bool aPositionSpecified, bool aSizeSpecified, @@ -125,7 +132,7 @@ parent: nsString aName, nsString aFeatures, nsString aBaseURI) - returns (bool windowIsNew, PBrowser window); + returns (bool windowOpened, FrameScriptInfo[] frameScripts); sync SyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows, Principal aPrincipal) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 78ca541baa54..7fa8042f0664 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1429,96 +1429,44 @@ TabChild::ProvideWindow(nsIDOMWindow* aParent, uint32_t aChromeFlags, // isn't a request to open a modal-type window, we're going to create a new // ' +} From 93138ddcc9e38ba26362e8366474714ec05e4c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Fri, 16 Jan 2015 22:24:45 +0100 Subject: [PATCH 126/133] Bug 1121550 - One-off button list header half visible when there's no one-off engine to display in the search panel, r=felipe. --- browser/base/content/urlbarBindings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 1955909fc6ec..0e9caeafc1c6 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -1151,7 +1151,8 @@ let header = document.getAnonymousElementByAttribute(this, "anonid", "search-panel-one-offs-header") - header.collapsed = list.collapsed = !engines.length; + // header is a xul:deck so collapsed doesn't work on it, see bug 589569. + header.hidden = list.collapsed = !engines.length; // 49px is the min-width of each search engine button, // adapt this const when changing the css. From 7c898408acfedecf2d4b724bad0820383f48c908 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 16 Jan 2015 14:20:11 -0800 Subject: [PATCH 127/133] Bug 1121297 (Part 1) - Move VolatileBuffer into libxul. r=glandium --HG-- rename : memory/mozalloc/VolatileBuffer.h => memory/volatile/VolatileBuffer.h rename : memory/mozalloc/VolatileBufferAshmem.cpp => memory/volatile/VolatileBufferAshmem.cpp rename : memory/mozalloc/VolatileBufferFallback.cpp => memory/volatile/VolatileBufferFallback.cpp rename : memory/mozalloc/VolatileBufferOSX.cpp => memory/volatile/VolatileBufferOSX.cpp rename : memory/mozalloc/VolatileBufferWindows.cpp => memory/volatile/VolatileBufferWindows.cpp rename : memory/mozalloc/moz.build => memory/volatile/moz.build rename : memory/mozalloc/tests/TestVolatileBuffer.cpp => memory/volatile/tests/TestVolatileBuffer.cpp rename : memory/mozalloc/tests/moz.build => memory/volatile/tests/moz.build --- memory/mozalloc/moz.build | 20 --- memory/mozalloc/tests/TestVolatileBuffer.cpp | 143 ------------------ .../{mozalloc => volatile}/VolatileBuffer.h | 2 +- .../VolatileBufferAshmem.cpp | 0 .../VolatileBufferFallback.cpp | 8 +- .../VolatileBufferOSX.cpp | 0 .../VolatileBufferWindows.cpp | 4 - memory/volatile/moz.build | 33 ++++ memory/volatile/tests/TestVolatileBuffer.cpp | 102 +++++++++++++ memory/{mozalloc => volatile}/tests/moz.build | 10 +- moz.build | 1 + 11 files changed, 150 insertions(+), 173 deletions(-) delete mode 100644 memory/mozalloc/tests/TestVolatileBuffer.cpp rename memory/{mozalloc => volatile}/VolatileBuffer.h (98%) rename memory/{mozalloc => volatile}/VolatileBufferAshmem.cpp (100%) rename memory/{mozalloc => volatile}/VolatileBufferFallback.cpp (90%) rename memory/{mozalloc => volatile}/VolatileBufferOSX.cpp (100%) rename memory/{mozalloc => volatile}/VolatileBufferWindows.cpp (97%) create mode 100644 memory/volatile/moz.build create mode 100644 memory/volatile/tests/TestVolatileBuffer.cpp rename memory/{mozalloc => volatile}/tests/moz.build (74%) diff --git a/memory/mozalloc/moz.build b/memory/mozalloc/moz.build index aae26cb08f6c..72640496c416 100644 --- a/memory/mozalloc/moz.build +++ b/memory/mozalloc/moz.build @@ -10,7 +10,6 @@ EXPORTS.mozilla += [ 'mozalloc.h', 'mozalloc_abort.h', 'mozalloc_oom.h', - 'VolatileBuffer.h', ] if CONFIG['MOZ_MSVC_STL_WRAP__RAISE'] or CONFIG['MOZ_MSVC_STL_WRAP__Throw']: @@ -40,23 +39,6 @@ UNIFIED_SOURCES += [ 'mozalloc_oom.cpp', ] -if CONFIG['OS_TARGET'] == 'Android': - UNIFIED_SOURCES += [ - 'VolatileBufferAshmem.cpp', - ] -elif CONFIG['OS_TARGET'] == 'Darwin': - UNIFIED_SOURCES += [ - 'VolatileBufferOSX.cpp', - ] -elif CONFIG['OS_TARGET'] == 'WINNT': - UNIFIED_SOURCES += [ - 'VolatileBufferWindows.cpp', - ] -else: - UNIFIED_SOURCES += [ - 'VolatileBufferFallback.cpp', - ] - if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': Library('mozalloc') else: @@ -66,8 +48,6 @@ else: # The strndup declaration in string.h is in an ifdef __USE_GNU section DEFINES['_GNU_SOURCE'] = True -TEST_DIRS += ['tests'] - GENERATED_INCLUDES += ['/xpcom'] DISABLE_STL_WRAPPING = True diff --git a/memory/mozalloc/tests/TestVolatileBuffer.cpp b/memory/mozalloc/tests/TestVolatileBuffer.cpp deleted file mode 100644 index 6bd3194295d0..000000000000 --- a/memory/mozalloc/tests/TestVolatileBuffer.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* 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 "TestHarness.h" -#include "mozilla/VolatileBuffer.h" -#include - -#if defined(ANDROID) -#include -#include -#include -#include -#include -#elif defined(XP_DARWIN) -#include -#endif - -using namespace mozilla; - -int main(int argc, char **argv) -{ - ScopedXPCOM xpcom("VolatileBufferTests"); - if (xpcom.failed()) { - return 1; - } - - RefPtr heapbuf = new VolatileBuffer(); - if (!heapbuf || !heapbuf->Init(512)) { - fail("Failed to initialize VolatileBuffer"); - return 1; - } - - { - VolatileBufferPtr ptr(heapbuf); - if (ptr.WasBufferPurged()) { - fail("Buffer was immediately purged after initialization"); - return 1; - } - - if (!ptr) { - fail("Didn't get a pointer"); - return 1; - } - } - - RefPtr buf = new VolatileBuffer(); - if (!buf || !buf->Init(16384)) { - fail("Failed to initialize VolatileBuffer"); - return 1; - } - - const char teststr[] = "foobar"; - - { - VolatileBufferPtr ptr(buf); - if (ptr.WasBufferPurged()) { - fail("Buffer should not be purged immediately after initialization"); - return 1; - } - - if (!ptr) { - fail("Didn't get a pointer"); - return 1; - } - - { - VolatileBufferPtr ptr2(buf); - if (ptr2.WasBufferPurged()) { - fail("Failed to Lock buffer again while currently locked"); - return 1; - } - - if (!ptr2) { - fail("Didn't get a pointer on the second lock"); - return 1; - } - - strcpy(ptr2, teststr); - } - } - - { - VolatileBufferPtr ptr(buf); - if (ptr.WasBufferPurged()) { - fail("Buffer was immediately purged after unlock"); - return 1; - } - - if (strcmp(ptr, teststr)) { - fail("Buffer failed to retain data after unlock"); - return 1; - } - } - - // Test purging if we know how to -#if defined(MOZ_WIDGET_GONK) - // This also works on Android, but we need root. - int fd = open("/" ASHMEM_NAME_DEF, O_RDWR); - if (fd < 0) { - fail("Failed to open ashmem device"); - return 1; - } - if (ioctl(fd, ASHMEM_PURGE_ALL_CACHES, NULL) < 0) { - fail("Failed to purge ashmem caches"); - return 1; - } -#elif defined(XP_DARWIN) - int state; - vm_purgable_control(mach_task_self(), (vm_address_t)NULL, - VM_PURGABLE_PURGE_ALL, &state); -#else - return 0; -#endif - - if (!buf->NonHeapSizeOfExcludingThis()) { - fail("Buffer should not be allocated on heap"); - return 1; - } - - { - VolatileBufferPtr ptr(buf); - if (!ptr.WasBufferPurged()) { - fail("Buffer should not be unpurged after forced purge"); - return 1; - } - - if (!strcmp(ptr, teststr)) { - fail("Purge did not actually purge data"); - return 1; - } - } - - { - VolatileBufferPtr ptr(buf); - if (ptr.WasBufferPurged()) { - fail("Buffer still purged after lock"); - return 1; - } - } - - return 0; -} diff --git a/memory/mozalloc/VolatileBuffer.h b/memory/volatile/VolatileBuffer.h similarity index 98% rename from memory/mozalloc/VolatileBuffer.h rename to memory/volatile/VolatileBuffer.h index 54bd88c743ae..76903b2cb16a 100644 --- a/memory/mozalloc/VolatileBuffer.h +++ b/memory/volatile/VolatileBuffer.h @@ -42,7 +42,7 @@ namespace mozilla { -class MOZALLOC_EXPORT VolatileBuffer : public RefCounted +class VolatileBuffer : public RefCounted { friend class VolatileBufferPtr_base; public: diff --git a/memory/mozalloc/VolatileBufferAshmem.cpp b/memory/volatile/VolatileBufferAshmem.cpp similarity index 100% rename from memory/mozalloc/VolatileBufferAshmem.cpp rename to memory/volatile/VolatileBufferAshmem.cpp diff --git a/memory/mozalloc/VolatileBufferFallback.cpp b/memory/volatile/VolatileBufferFallback.cpp similarity index 90% rename from memory/mozalloc/VolatileBufferFallback.cpp rename to memory/volatile/VolatileBufferFallback.cpp index 9445b6067399..f1d1bfd53676 100644 --- a/memory/mozalloc/VolatileBufferFallback.cpp +++ b/memory/volatile/VolatileBufferFallback.cpp @@ -27,9 +27,13 @@ bool VolatileBuffer::Init(size_t aSize, size_t aAlignment) mSize = aSize; #if defined(MOZ_MEMORY) - posix_memalign(&mBuf, aAlignment, aSize); + if (posix_memalign(&mBuf, aAlignment, aSize) != 0) { + return false; + } #elif defined(HAVE_POSIX_MEMALIGN) - (void)moz_posix_memalign(&mBuf, aAlignment, aSize); + if (moz_posix_memalign(&mBuf, aAlignment, aSize) != 0) { + return false; + } #else #error "No memalign implementation found" #endif diff --git a/memory/mozalloc/VolatileBufferOSX.cpp b/memory/volatile/VolatileBufferOSX.cpp similarity index 100% rename from memory/mozalloc/VolatileBufferOSX.cpp rename to memory/volatile/VolatileBufferOSX.cpp diff --git a/memory/mozalloc/VolatileBufferWindows.cpp b/memory/volatile/VolatileBufferWindows.cpp similarity index 97% rename from memory/mozalloc/VolatileBufferWindows.cpp rename to memory/volatile/VolatileBufferWindows.cpp index 2aba1e2747ff..c7324b0cec35 100644 --- a/memory/mozalloc/VolatileBufferWindows.cpp +++ b/memory/volatile/VolatileBufferWindows.cpp @@ -2,10 +2,6 @@ * 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/. */ -#if defined(XP_WIN) -# define MOZALLOC_EXPORT __declspec(dllexport) -#endif - #include "VolatileBuffer.h" #include "mozilla/Assertions.h" #include "mozilla/mozalloc.h" diff --git a/memory/volatile/moz.build b/memory/volatile/moz.build new file mode 100644 index 000000000000..044c9f4dd88a --- /dev/null +++ b/memory/volatile/moz.build @@ -0,0 +1,33 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. +NO_VISIBILITY_FLAGS = True + +EXPORTS.mozilla += [ + 'VolatileBuffer.h', +] + +if CONFIG['OS_TARGET'] == 'Android': + UNIFIED_SOURCES += [ + 'VolatileBufferAshmem.cpp', + ] +elif CONFIG['OS_TARGET'] == 'Darwin': + UNIFIED_SOURCES += [ + 'VolatileBufferOSX.cpp', + ] +elif CONFIG['OS_TARGET'] == 'WINNT': + UNIFIED_SOURCES += [ + 'VolatileBufferWindows.cpp', + ] +else: + UNIFIED_SOURCES += [ + 'VolatileBufferFallback.cpp', + ] + +FINAL_LIBRARY = 'xul' + +TEST_DIRS += ['tests'] + +FAIL_ON_WARNINGS = True diff --git a/memory/volatile/tests/TestVolatileBuffer.cpp b/memory/volatile/tests/TestVolatileBuffer.cpp new file mode 100644 index 000000000000..9a4a8781d7f8 --- /dev/null +++ b/memory/volatile/tests/TestVolatileBuffer.cpp @@ -0,0 +1,102 @@ +/* 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 "gtest/gtest.h" +#include "mozilla/VolatileBuffer.h" +#include + +#if defined(ANDROID) +#include +#include +#include +#include +#include +#elif defined(XP_DARWIN) +#include +#endif + +using namespace mozilla; + +TEST(VolatileBufferTest, HeapVolatileBuffersWork) +{ + RefPtr heapbuf = new VolatileBuffer(); + + ASSERT_TRUE(heapbuf) << "Failed to create VolatileBuffer"; + ASSERT_TRUE(heapbuf->Init(512)) << "Failed to initialize VolatileBuffer"; + + VolatileBufferPtr ptr(heapbuf); + + EXPECT_FALSE(ptr.WasBufferPurged()) + << "Buffer should not be purged immediately after initialization"; + EXPECT_TRUE(ptr) << "Couldn't get pointer from VolatileBufferPtr"; +} + +TEST(VolatileBufferTest, RealVolatileBuffersWork) +{ + RefPtr buf = new VolatileBuffer(); + + ASSERT_TRUE(buf) << "Failed to create VolatileBuffer"; + ASSERT_TRUE(buf->Init(16384)) << "Failed to initialize VolatileBuffer"; + + const char teststr[] = "foobar"; + + { + VolatileBufferPtr ptr(buf); + + EXPECT_FALSE(ptr.WasBufferPurged()) + << "Buffer should not be purged immediately after initialization"; + EXPECT_TRUE(ptr) << "Couldn't get pointer from VolatileBufferPtr"; + + { + VolatileBufferPtr ptr2(buf); + + EXPECT_FALSE(ptr.WasBufferPurged()) + << "Failed to lock buffer again while currently locked"; + ASSERT_TRUE(ptr2) << "Didn't get a pointer on the second lock"; + + strcpy(ptr2, teststr); + } + } + + { + VolatileBufferPtr ptr(buf); + + EXPECT_FALSE(ptr.WasBufferPurged()) + << "Buffer was immediately purged after unlock"; + EXPECT_STREQ(ptr, teststr) << "Buffer failed to retain data after unlock"; + } + + // Test purging if we know how to +#if defined(MOZ_WIDGET_GONK) + // This also works on Android, but we need root. + int fd = open("/" ASHMEM_NAME_DEF, O_RDWR); + + ASSERT_GE(fd, 0) << "Failed to open ashmem device"; + ASSERT_GE(ioctl(fd, ASHMEM_PURGE_ALL_CACHES, NULL), 0) + << "Failed to purge ashmem caches"; +#elif defined(XP_DARWIN) + int state; + vm_purgable_control(mach_task_self(), (vm_address_t)NULL, + VM_PURGABLE_PURGE_ALL, &state); +#else + return; +#endif + + EXPECT_GT(buf->NonHeapSizeOfExcludingThis(), 0ul) + << "Buffer should not be allocated on heap"; + + { + VolatileBufferPtr ptr(buf); + + EXPECT_TRUE(ptr.WasBufferPurged()) + << "Buffer should not be unpurged after forced purge"; + EXPECT_STRNE(ptr, teststr) << "Purge did not actually purge data"; + } + + { + VolatileBufferPtr ptr(buf); + + EXPECT_FALSE(ptr.WasBufferPurged()) << "Buffer still purged after lock"; + } +} diff --git a/memory/mozalloc/tests/moz.build b/memory/volatile/tests/moz.build similarity index 74% rename from memory/mozalloc/tests/moz.build rename to memory/volatile/tests/moz.build index 3627343462c5..e792d6887e52 100644 --- a/memory/mozalloc/tests/moz.build +++ b/memory/volatile/tests/moz.build @@ -4,6 +4,10 @@ # 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/. -GeckoCppUnitTests([ - 'TestVolatileBuffer', -]) +UNIFIED_SOURCES = [ + 'TestVolatileBuffer.cpp', +] + +FINAL_LIBRARY = 'xul-gtest' + +FAIL_ON_WARNINGS = True diff --git a/moz.build b/moz.build index d5d7cf32b305..1c97f6f9b5c0 100644 --- a/moz.build +++ b/moz.build @@ -44,6 +44,7 @@ if not CONFIG['LIBXUL_SDK']: DIRS += [ 'mozglue', 'memory/mozalloc', + 'memory/volatile', ] if not CONFIG['JS_STANDALONE']: From 64793c37ee9ac0be6ba255bffb691cd3c41936d1 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 16 Jan 2015 14:20:14 -0800 Subject: [PATCH 128/133] Bug 1121297 (Part 2) - Make VolatileBuffer threadsafe. r=glandium --- memory/volatile/VolatileBuffer.h | 18 ++++++++++++++---- memory/volatile/VolatileBufferAshmem.cpp | 9 ++++++++- memory/volatile/VolatileBufferFallback.cpp | 9 ++++++++- memory/volatile/VolatileBufferOSX.cpp | 9 ++++++++- memory/volatile/VolatileBufferWindows.cpp | 9 ++++++++- 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/memory/volatile/VolatileBuffer.h b/memory/volatile/VolatileBuffer.h index 76903b2cb16a..e82f755a824b 100644 --- a/memory/volatile/VolatileBuffer.h +++ b/memory/volatile/VolatileBuffer.h @@ -6,6 +6,7 @@ #define mozalloc_VolatileBuffer_h #include "mozilla/mozalloc.h" +#include "mozilla/Mutex.h" #include "mozilla/RefPtr.h" #include "mozilla/MemoryReporting.h" @@ -36,19 +37,19 @@ * When a buffer is purged, some or all of the buffer is zeroed out. This * API cannot tell which parts of the buffer were lost. * - * VolatileBuffer is not thread safe. Do not use VolatileBufferPtrs on - * different threads. + * VolatileBuffer and VolatileBufferPtr are threadsafe. */ namespace mozilla { -class VolatileBuffer : public RefCounted +class VolatileBuffer { friend class VolatileBufferPtr_base; public: MOZ_DECLARE_REFCOUNTED_TYPENAME(VolatileBuffer) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VolatileBuffer) + VolatileBuffer(); - ~VolatileBuffer(); /* aAlignment must be a multiple of the pointer size */ bool Init(size_t aSize, size_t aAlignment = sizeof(void*)); @@ -62,6 +63,15 @@ protected: void Unlock(); private: + ~VolatileBuffer(); + + /** + * Protects mLockCount, mFirstLock, and changes to the volatility of our + * buffer. Other member variables are read-only except in Init() and the + * destructor. + */ + Mutex mMutex; + void* mBuf; size_t mSize; int mLockCount; diff --git a/memory/volatile/VolatileBufferAshmem.cpp b/memory/volatile/VolatileBufferAshmem.cpp index ea34df263862..09904d6efe01 100644 --- a/memory/volatile/VolatileBufferAshmem.cpp +++ b/memory/volatile/VolatileBufferAshmem.cpp @@ -22,7 +22,8 @@ extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size); namespace mozilla { VolatileBuffer::VolatileBuffer() - : mBuf(nullptr) + : mMutex("VolatileBuffer") + , mBuf(nullptr) , mSize(0) , mLockCount(0) , mFd(-1) @@ -72,6 +73,8 @@ heap_alloc: VolatileBuffer::~VolatileBuffer() { + MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?"); + if (OnHeap()) { free(mBuf); } else { @@ -83,6 +86,8 @@ VolatileBuffer::~VolatileBuffer() bool VolatileBuffer::Lock(void** aBuf) { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer"); *aBuf = mBuf; @@ -98,6 +103,8 @@ VolatileBuffer::Lock(void** aBuf) void VolatileBuffer::Unlock() { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!"); if (--mLockCount || OnHeap()) { return; diff --git a/memory/volatile/VolatileBufferFallback.cpp b/memory/volatile/VolatileBufferFallback.cpp index f1d1bfd53676..f4bfe39c6487 100644 --- a/memory/volatile/VolatileBufferFallback.cpp +++ b/memory/volatile/VolatileBufferFallback.cpp @@ -13,7 +13,8 @@ int posix_memalign(void** memptr, size_t alignment, size_t size); namespace mozilla { VolatileBuffer::VolatileBuffer() - : mBuf(nullptr) + : mMutex("VolatileBuffer") + , mBuf(nullptr) , mSize(0) , mLockCount(0) { @@ -42,12 +43,16 @@ bool VolatileBuffer::Init(size_t aSize, size_t aAlignment) VolatileBuffer::~VolatileBuffer() { + MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?"); + free(mBuf); } bool VolatileBuffer::Lock(void** aBuf) { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer"); *aBuf = mBuf; @@ -59,6 +64,8 @@ VolatileBuffer::Lock(void** aBuf) void VolatileBuffer::Unlock() { + MutexAutoLock lock(mMutex); + mLockCount--; MOZ_ASSERT(mLockCount >= 0, "VolatileBuffer unlocked too many times!"); } diff --git a/memory/volatile/VolatileBufferOSX.cpp b/memory/volatile/VolatileBufferOSX.cpp index 3f567d4f35ba..af39bcae166b 100644 --- a/memory/volatile/VolatileBufferOSX.cpp +++ b/memory/volatile/VolatileBufferOSX.cpp @@ -16,7 +16,8 @@ namespace mozilla { VolatileBuffer::VolatileBuffer() - : mBuf(nullptr) + : mMutex("VolatileBuffer") + , mBuf(nullptr) , mSize(0) , mLockCount(0) , mHeap(false) @@ -53,6 +54,8 @@ heap_alloc: VolatileBuffer::~VolatileBuffer() { + MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?"); + if (OnHeap()) { free(mBuf); } else { @@ -63,6 +66,8 @@ VolatileBuffer::~VolatileBuffer() bool VolatileBuffer::Lock(void** aBuf) { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer"); *aBuf = mBuf; @@ -82,6 +87,8 @@ VolatileBuffer::Lock(void** aBuf) void VolatileBuffer::Unlock() { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!"); if (--mLockCount || OnHeap()) { return; diff --git a/memory/volatile/VolatileBufferWindows.cpp b/memory/volatile/VolatileBufferWindows.cpp index c7324b0cec35..b12e0eccb533 100644 --- a/memory/volatile/VolatileBufferWindows.cpp +++ b/memory/volatile/VolatileBufferWindows.cpp @@ -22,7 +22,8 @@ extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size); namespace mozilla { VolatileBuffer::VolatileBuffer() - : mBuf(nullptr) + : mMutex("VolatileBuffer") + , mBuf(nullptr) , mSize(0) , mLockCount(0) , mHeap(false) @@ -68,6 +69,8 @@ heap_alloc: VolatileBuffer::~VolatileBuffer() { + MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?"); + if (OnHeap()) { #ifdef MOZ_MEMORY free(mBuf); @@ -82,6 +85,8 @@ VolatileBuffer::~VolatileBuffer() bool VolatileBuffer::Lock(void** aBuf) { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer"); *aBuf = mBuf; @@ -107,6 +112,8 @@ VolatileBuffer::Lock(void** aBuf) void VolatileBuffer::Unlock() { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!"); if (--mLockCount || OnHeap()) { return; From 180ec332769137d04f2b87c7dc8955ec7209cafd Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Fri, 16 Jan 2015 14:25:58 -0800 Subject: [PATCH 129/133] Backout CGC (Bug 650161, ae2eec4a74ea) to deal with a couple crashes that cropped up with wider exposure. --- js/src/configure.in | 12 ++++++++++++ js/src/gc/GCInternals.h | 2 ++ js/src/gc/GCRuntime.h | 15 +++++++++++++++ js/src/gc/Heap.h | 2 ++ js/src/gc/Marking.cpp | 16 +++++++++++++++- js/src/js-config.h.in | 3 +++ js/src/jsapi-tests/testWeakMap.cpp | 6 ++++++ js/src/jscompartment.cpp | 4 ++++ js/src/jscompartment.h | 2 ++ js/src/jsgc.cpp | 29 +++++++++++++++++++++++++++-- js/src/jsgc.h | 11 +++++++++++ js/src/jsinfer.cpp | 4 ++++ js/src/jsinfer.h | 5 +++++ js/src/jspropertytree.cpp | 4 ++++ js/src/jspubtd.h | 3 ++- js/src/vm/Shape-inl.h | 2 ++ js/src/vm/Shape.cpp | 4 ++++ js/src/vm/Shape.h | 8 ++++++++ 18 files changed, 128 insertions(+), 4 deletions(-) diff --git a/js/src/configure.in b/js/src/configure.in index 23b455d1ebb3..a7c981b5e611 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -3054,6 +3054,18 @@ MOZ_ARG_WITH_STRING(wrap-malloc, [ --with-wrap-malloc=DIR Location of malloc wrapper library], WRAP_LDFLAGS="${WRAP_LDFLAGS} $withval") +dnl ======================================================== +dnl = Use compacting GC +dnl ======================================================== +dnl Compact the heap by moving GC things when doing a shrinking colletion. +MOZ_ARG_ENABLE_BOOL(gccompacting, +[ --enable-gccompacting Compact the heap by moving GC things], + JSGC_COMPACTING=1, + JSGC_COMPACTING= ) +if test -n "$JSGC_COMPACTING"; then + AC_DEFINE(JSGC_COMPACTING) +fi + dnl ======================================================== dnl = Use a smaller chunk size for GC chunks dnl ======================================================== diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index 15b85f26dae4..0a718a9ac32b 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -143,6 +143,7 @@ void CheckHashTablesAfterMovingGC(JSRuntime *rt); #endif +#ifdef JSGC_COMPACTING struct MovingTracer : JSTracer { explicit MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapKeysValues) {} @@ -151,6 +152,7 @@ struct MovingTracer : JSTracer { return trc->callback == Visit; } }; +#endif } /* namespace gc */ } /* namespace js */ diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index b2443f7a4548..b493829df86a 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -32,8 +32,11 @@ struct FinalizePhase; class MarkingValidator; struct AutoPrepareForTracing; class AutoTraceSession; + +#ifdef JSGC_COMPACTING struct ArenasToUpdate; struct MovingTracer; +#endif class ChunkPool { @@ -292,7 +295,11 @@ class GCRuntime bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; } bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; } bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); } +#ifdef JSGC_COMPACTING bool isHeapCompacting() { return isHeapMajorCollecting() && state() == COMPACT; } +#else + bool isHeapCompacting() { return false; } +#endif bool triggerGC(JS::gcreason::Reason reason); void maybeAllocTriggerZoneGC(Zone *zone, const AutoLockGC &lock); @@ -428,9 +435,11 @@ class GCRuntime void disableGenerationalGC(); void enableGenerationalGC(); +#ifdef JSGC_COMPACTING void disableCompactingGC(); void enableCompactingGC(); bool isCompactingGCEnabled(); +#endif void setGrayRootsTracer(JSTraceDataOp traceOp, void *data); bool addBlackRootsTracer(JSTraceDataOp traceOp, void *data); @@ -592,6 +601,7 @@ class GCRuntime void assertBackgroundSweepingFinished(); bool shouldCompact(); bool compactPhase(bool lastGC); +#ifdef JSGC_COMPACTING void sweepTypesAfterCompacting(Zone *zone); void sweepZoneAfterCompacting(Zone *zone); ArenaHeader *relocateArenas(); @@ -603,6 +613,7 @@ class GCRuntime #ifdef DEBUG void protectRelocatedArenas(ArenaHeader *relocatedList); void unprotectRelocatedArenas(ArenaHeader *relocatedList); +#endif #endif void finishCollection(); @@ -799,11 +810,13 @@ class GCRuntime */ unsigned generationalDisabled; +#ifdef JSGC_COMPACTING /* * Some code cannot tolerate compacting GC so it can be disabled with this * counter. */ unsigned compactingDisabled; +#endif /* * This is true if we are in the middle of a brain transplant (e.g., @@ -904,7 +917,9 @@ class GCRuntime size_t noGCOrAllocationCheck; +#ifdef JSGC_COMPACTING ArenaHeader* relocatedArenasToRelease; +#endif #endif diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index 6e53c16948be..f18d82cd9325 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -642,8 +642,10 @@ struct ArenaHeader void unmarkAll(); +#ifdef JSGC_COMPACTING size_t countUsedCells(); size_t countFreeCells(); +#endif }; static_assert(ArenaZoneOffset == offsetof(ArenaHeader, zone), "The hardcoded API zone offset must match the actual offset."); diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 0cad8d8dd711..cd85aece5966 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -161,14 +161,18 @@ CheckMarkedThing(JSTracer *trc, T **thingp) T *thing = *thingp; MOZ_ASSERT(*thingp); +#ifdef JSGC_COMPACTING thing = MaybeForwarded(thing); +#endif /* This function uses data that's not available in the nursery. */ if (IsInsideNursery(thing)) return; +#ifdef JSGC_COMPACTING MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc) && !Nursery::IsMinorCollectionTracer(trc), !IsForwarded(*thingp)); +#endif /* * Permanent atoms are not associated with this runtime, but will be ignored @@ -180,8 +184,13 @@ CheckMarkedThing(JSTracer *trc, T **thingp) Zone *zone = thing->zoneFromAnyThread(); JSRuntime *rt = trc->runtime(); +#ifdef JSGC_COMPACTING MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessZone(zone)); MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessRuntime(rt)); +#else + MOZ_ASSERT(CurrentThreadCanAccessZone(zone)); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); +#endif MOZ_ASSERT(zone->runtimeFromAnyThread() == trc->runtime()); MOZ_ASSERT(trc->hasTracingDetails()); @@ -428,8 +437,10 @@ IsMarkedFromAnyThread(T **thingp) Zone *zone = (*thingp)->asTenured().zoneFromAnyThread(); if (!zone->isCollectingFromAnyThread() || zone->isGCFinished()) return true; +#ifdef JSGC_COMPACTING if (zone->isGCCompacting() && IsForwarded(*thingp)) *thingp = Forwarded(*thingp); +#endif return (*thingp)->asTenured().isMarked(); } @@ -470,10 +481,12 @@ IsAboutToBeFinalizedFromAnyThread(T **thingp) return false; return !thing->asTenured().isMarked(); } +#ifdef JSGC_COMPACTING else if (zone->isGCCompacting() && IsForwarded(thing)) { *thingp = Forwarded(thing); return false; } +#endif return false; } @@ -491,10 +504,11 @@ UpdateIfRelocated(JSRuntime *rt, T **thingp) return *thingp; } +#ifdef JSGC_COMPACTING Zone *zone = (*thingp)->zone(); if (zone->isGCCompacting() && IsForwarded(*thingp)) *thingp = Forwarded(*thingp); - +#endif return *thingp; } diff --git a/js/src/js-config.h.in b/js/src/js-config.h.in index e7e3e1ed2562..b7a7e1a9f3cd 100644 --- a/js/src/js-config.h.in +++ b/js/src/js-config.h.in @@ -31,6 +31,9 @@ /* Define to 1 if SpiderMonkey should use small chunks. */ #undef JS_GC_SMALL_CHUNK_SIZE +/* Define to 1 if SpiderMonkey should use Compacting GC. */ +#undef JSGC_COMPACTING + /* Define to 1 if the header is present and useable. See jscpucfg.h. */ #undef JS_HAVE_ENDIAN_H diff --git a/js/src/jsapi-tests/testWeakMap.cpp b/js/src/jsapi-tests/testWeakMap.cpp index b49967e9cbf1..a337bcc3ec63 100644 --- a/js/src/jsapi-tests/testWeakMap.cpp +++ b/js/src/jsapi-tests/testWeakMap.cpp @@ -66,6 +66,10 @@ checkSize(JS::HandleObject map, uint32_t expected) } END_TEST(testWeakMap_basicOperations) +// TODO: this test stores object pointers in a private slot which is not marked +// and so doesn't work with compacting GC. +#ifndef JSGC_COMPACTING + BEGIN_TEST(testWeakMap_keyDelegates) { JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); @@ -249,3 +253,5 @@ checkSize(JS::HandleObject map, uint32_t expected) return true; } END_TEST(testWeakMap_keyDelegates) + +#endif diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index d579615db44c..b267be3d0ca2 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -649,6 +649,8 @@ JSCompartment::sweepCrossCompartmentWrappers() } } +#ifdef JSGC_COMPACTING + void JSCompartment::fixupAfterMovingGC() { fixupGlobal(); @@ -665,6 +667,8 @@ JSCompartment::fixupGlobal() global_.set(MaybeForwarded(global)); } +#endif // JSGC_COMPACTING + void JSCompartment::purge() { diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 732c445859f9..c6968cd45e22 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -398,10 +398,12 @@ struct JSCompartment void purge(); void clearTables(); +#ifdef JSGC_COMPACTING void fixupInitialShapeTable(); void fixupNewTypeObjectTable(js::types::NewTypeObjectTable &table); void fixupAfterMovingGC(); void fixupGlobal(); +#endif bool hasObjectMetadataCallback() const { return objectMetadataCallback; } void setObjectMetadataCallback(js::ObjectMetadataCallback callback); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index bf7b0de8273c..075370d2e962 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1129,7 +1129,9 @@ GCRuntime::GCRuntime(JSRuntime *rt) : sliceBudget(SliceBudget::Unlimited), incrementalAllowed(true), generationalDisabled(0), +#ifdef JSGC_COMPACTING compactingDisabled(0), +#endif manipulatingDeadZones(false), objectsMarkedInDeadZones(0), poked(false), @@ -1149,7 +1151,9 @@ GCRuntime::GCRuntime(JSRuntime *rt) : #ifdef DEBUG inUnsafeRegion(0), noGCOrAllocationCheck(0), +#ifdef JSGC_COMPACTING relocatedArenasToRelease(nullptr), +#endif #endif lock(nullptr), lockOwner(nullptr), @@ -1943,9 +1947,15 @@ ArenaLists::allocateFromArenaInner(JS::Zone *zone, ArenaHeader *aheader, AllocKi bool GCRuntime::shouldCompact() { +#ifdef JSGC_COMPACTING return invocationKind == GC_SHRINK && isCompactingGCEnabled(); +#else + return false; +#endif } +#ifdef JSGC_COMPACTING + void GCRuntime::disableCompactingGC() { @@ -2704,10 +2714,12 @@ GCRuntime::releaseRelocatedArenasWithoutUnlocking(ArenaHeader *relocatedList, co } } +#endif // JSGC_COMPACTING + void GCRuntime::releaseHeldRelocatedArenas() { -#ifdef DEBUG +#if defined(JSGC_COMPACTING) && defined(DEBUG) // In debug mode we don't release relocated arenas straight away. Instead // we protect them and hold onto them until the next GC sweep phase to catch // any pointers to them that didn't get forwarded. @@ -5479,6 +5491,9 @@ GCRuntime::endSweepPhase(bool lastGC) bool GCRuntime::compactPhase(bool lastGC) { +#ifndef JSGC_COMPACTING + MOZ_CRASH(); +#else gcstats::AutoPhase ap(stats, gcstats::PHASE_COMPACT); if (isIncremental) { @@ -5543,6 +5558,8 @@ GCRuntime::compactPhase(bool lastGC) } } #endif + +#endif // JSGC_COMPACTING return true; } @@ -5702,6 +5719,7 @@ GCRuntime::resetIncrementalGC(const char *reason) break; } +#ifdef JSGC_COMPACTING case COMPACT: { { gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD); @@ -5717,6 +5735,7 @@ GCRuntime::resetIncrementalGC(const char *reason) invocationKind = oldInvocationKind; break; } +#endif default: MOZ_CRASH("Invalid incremental GC state"); @@ -6404,7 +6423,7 @@ GCRuntime::onOutOfMallocMemory(const AutoLockGC &lock) { // Release any relocated arenas we may be holding on to, without releasing // the GC lock. -#ifdef DEBUG +#if defined(JSGC_COMPACTING) && defined(DEBUG) unprotectRelocatedArenas(relocatedArenasToRelease); releaseRelocatedArenasWithoutUnlocking(relocatedArenasToRelease, lock); relocatedArenasToRelease = nullptr; @@ -7125,13 +7144,19 @@ JS::IsIncrementalGCEnabled(JSRuntime *rt) JS_PUBLIC_API(void) JS::DisableCompactingGC(JSRuntime *rt) { +#ifdef JSGC_COMPACTING rt->gc.disableCompactingGC(); +#endif } JS_PUBLIC_API(bool) JS::IsCompactingGCEnabled(JSRuntime *rt) { +#ifdef JSGC_COMPACTING return rt->gc.isCompactingGCEnabled(); +#else + return false; +#endif } JS_PUBLIC_API(bool) diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 7cb3c6ed1649..0bd8f37e1982 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -454,9 +454,11 @@ class ArenaList { return *this; } +#ifdef JSGC_COMPACTING ArenaHeader *removeRemainingArenas(ArenaHeader **arenap, const AutoLockGC &lock); ArenaHeader *pickArenasToRelocate(JSRuntime *runtime); ArenaHeader *relocateArenas(ArenaHeader *toRelocate, ArenaHeader *relocated); +#endif }; /* @@ -783,7 +785,9 @@ class ArenaLists MOZ_ASSERT(freeLists[kind].isEmpty()); } +#ifdef JSGC_COMPACTING ArenaHeader *relocateArenas(ArenaHeader *relocatedList); +#endif void queueForegroundObjectsForSweep(FreeOp *fop); void queueForegroundThingsForSweep(FreeOp *fop); @@ -1266,7 +1270,9 @@ inline void CheckGCThingAfterMovingGC(T *t) { MOZ_ASSERT_IF(t, !IsInsideNursery(t)); +#ifdef JSGC_COMPACTING MOZ_ASSERT_IF(t, !IsForwarded(t)); +#endif } inline void @@ -1423,11 +1429,16 @@ struct AutoDisableProxyCheck struct AutoDisableCompactingGC { +#ifdef JSGC_COMPACTING explicit AutoDisableCompactingGC(JSRuntime *rt); ~AutoDisableCompactingGC(); private: gc::GCRuntime &gc; +#else + explicit AutoDisableCompactingGC(JSRuntime *rt) {} + ~AutoDisableCompactingGC() {} +#endif }; void diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 0ea1d8d0dfa3..8900558dc1be 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -4891,6 +4891,8 @@ JSCompartment::sweepNewTypeObjectTable(NewTypeObjectTable &table) } } +#ifdef JSGC_COMPACTING + void JSCompartment::fixupNewTypeObjectTable(NewTypeObjectTable &table) { @@ -4964,6 +4966,8 @@ TypeObject::fixupAfterMovingGC() } } +#endif // JSGC_COMPACTING + #ifdef JSGC_HASH_TABLE_CHECKS void diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 2c42fd432b75..3d10cbae2961 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -925,7 +925,10 @@ class TypeNewScript void trace(JSTracer *trc); void sweep(); + +#ifdef JSGC_COMPACTING void fixupAfterMovingGC(); +#endif void registerNewObject(PlainObject *res); void unregisterNewObject(PlainObject *res); @@ -1238,7 +1241,9 @@ struct TypeObject : public gc::TenuredCell flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT; } +#ifdef JSGC_COMPACTING void fixupAfterMovingGC(); +#endif size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; diff --git a/js/src/jspropertytree.cpp b/js/src/jspropertytree.cpp index 8dcb4b6ebbd4..9a3d005c4b71 100644 --- a/js/src/jspropertytree.cpp +++ b/js/src/jspropertytree.cpp @@ -235,6 +235,8 @@ Shape::finalize(FreeOp *fop) fop->delete_(kids.toHash()); } +#ifdef JSGC_COMPACTING + void Shape::fixupDictionaryShapeAfterMovingGC() { @@ -320,6 +322,8 @@ Shape::fixupAfterMovingGC() fixupShapeTreeAfterMovingGC(); } +#endif // JSGC_COMPACTING + void ShapeGetterSetterRef::mark(JSTracer *trc) { diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 15e83e6e752e..be8894f126ea 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -20,7 +20,8 @@ #include "js/TypeDecls.h" -#if (defined(JS_GC_ZEAL)) || defined(DEBUG) +#if (defined(JS_GC_ZEAL)) || \ + (defined(JSGC_COMPACTING) && defined(DEBUG)) # define JSGC_HASH_TABLE_CHECKS #endif diff --git a/js/src/vm/Shape-inl.h b/js/src/vm/Shape-inl.h index 0b5c3ebeb807..a17755fed1ce 100644 --- a/js/src/vm/Shape-inl.h +++ b/js/src/vm/Shape-inl.h @@ -224,12 +224,14 @@ GetShapeAttributes(JSObject *obj, Shape *shape) return shape->attributes(); } +#ifdef JSGC_COMPACTING inline void BaseShape::fixupAfterMovingGC() { if (hasTable()) table().fixupAfterMovingGC(); } +#endif } /* namespace js */ diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 5caf797d9287..70b86fbb4505 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -256,6 +256,7 @@ ShapeTable::search(jsid id, bool adding) MOZ_CRASH("Shape::search failed to find an expected entry."); } +#ifdef JSGC_COMPACTING void ShapeTable::fixupAfterMovingGC() { @@ -267,6 +268,7 @@ ShapeTable::fixupAfterMovingGC() entry.setPreservingCollision(Forwarded(shape)); } } +#endif bool ShapeTable::change(int log2Delta, ExclusiveContext *cx) @@ -1691,6 +1693,7 @@ JSCompartment::sweepInitialShapeTable() } } +#ifdef JSGC_COMPACTING void JSCompartment::fixupInitialShapeTable() { @@ -1729,6 +1732,7 @@ JSCompartment::fixupInitialShapeTable() } } } +#endif // JSGC_COMPACTING void AutoRooterGetterSetter::Inner::trace(JSTracer *trc) diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index a7416b02673d..37cf4e15fa74 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -227,8 +227,10 @@ class ShapeTable { bool change(int log2Delta, ExclusiveContext *cx); Entry &search(jsid id, bool adding); +#ifdef JSGC_COMPACTING /* Update entries whose shapes have been moved */ void fixupAfterMovingGC(); +#endif private: Entry &getEntry(uint32_t i) const { @@ -528,7 +530,9 @@ class BaseShape : public gc::TenuredCell gc::MarkObject(trc, &metadata, "metadata"); } +#ifdef JSGC_COMPACTING void fixupAfterMovingGC(); +#endif private: static void staticAsserts() { @@ -1057,7 +1061,9 @@ class Shape : public gc::TenuredCell inline Shape *search(ExclusiveContext *cx, jsid id); inline Shape *searchLinear(jsid id); +#ifdef JSGC_COMPACTING void fixupAfterMovingGC(); +#endif /* For JIT usage */ static inline size_t offsetOfBase() { return offsetof(Shape, base_); } @@ -1065,8 +1071,10 @@ class Shape : public gc::TenuredCell static inline uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; } private: +#ifdef JSGC_COMPACTING void fixupDictionaryShapeAfterMovingGC(); void fixupShapeTreeAfterMovingGC(); +#endif static void staticAsserts() { JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base)); From cb8c3ea7503ad25bd22466e3853c0e56a22301ad Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 16 Jan 2015 14:46:05 -0800 Subject: [PATCH 130/133] Backout bug 1118618 on a CLOSED TREE --- browser/app/profile/firefox.js | 10 +- browser/base/content/browser.js | 3 - browser/base/content/browser.xul | 20 - browser/base/content/tabbrowser.xml | 4 - browser/components/nsBrowserGlue.js | 5 - browser/devtools/framework/gDevTools.jsm | 44 +- .../locales/en-US/chrome/browser/browser.dtd | 8 - .../en-US/chrome/browser/browser.properties | 5 - browser/modules/ProcessHangMonitor.jsm | 309 ------ browser/modules/moz.build | 1 - dom/base/SlowScriptDebug.js | 3 - dom/base/nsGlobalWindow.cpp | 39 +- dom/base/nsGlobalWindow.h | 1 - dom/base/nsISlowScriptDebug.idl | 15 - dom/ipc/ContentChild.cpp | 8 - dom/ipc/ContentChild.h | 4 - dom/ipc/ContentParent.cpp | 21 +- dom/ipc/ContentParent.h | 6 - dom/ipc/PContent.ipdl | 2 - dom/ipc/PProcessHangMonitor.ipdl | 42 - dom/ipc/ProcessHangMonitor.cpp | 954 ------------------ dom/ipc/ProcessHangMonitor.h | 78 -- dom/ipc/ProcessHangMonitorIPC.h | 34 - dom/ipc/moz.build | 10 - dom/ipc/nsIHangReport.idl | 66 -- dom/plugins/base/nsPluginHost.h | 3 +- dom/plugins/ipc/PluginBridge.h | 3 - dom/plugins/ipc/PluginModuleParent.cpp | 64 +- dom/plugins/ipc/PluginModuleParent.h | 17 +- ipc/glue/MessageChannel.cpp | 15 +- ipc/glue/MessageChannel.h | 8 - ipc/glue/MessageLink.h | 5 - js/xpconnect/src/XPCJSRuntime.cpp | 24 +- modules/libpref/init/all.js | 5 - .../client/marionette/geckoinstance.py | 3 +- testing/profiles/prefs_general.js | 2 - 36 files changed, 38 insertions(+), 1803 deletions(-) delete mode 100644 browser/modules/ProcessHangMonitor.jsm delete mode 100644 dom/ipc/PProcessHangMonitor.ipdl delete mode 100644 dom/ipc/ProcessHangMonitor.cpp delete mode 100644 dom/ipc/ProcessHangMonitor.h delete mode 100644 dom/ipc/ProcessHangMonitorIPC.h delete mode 100644 dom/ipc/nsIHangReport.idl diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index d222d2e61149..14f6ac7ce3e8 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1803,11 +1803,5 @@ pref("extensions.interposition.prefetching", true); pref("browser.defaultbrowser.notificationbar", false); -// How often to check for CPOW timeouts. CPOWs are only timed out by -// the hang monitor. -pref("dom.ipc.cpow.timeout", 500); - -// Enable e10s hang monitoring (slow script checking and plugin hang -// detection). -pref("dom.ipc.processHangMonitor", true); -pref("dom.ipc.reportProcessHangs", true); +// How many milliseconds to wait for a CPOW response from the child process. +pref("dom.ipc.cpow.timeout", 0); diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index b7781a58708e..ca9db831b348 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -149,9 +149,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Social", XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs", "resource://gre/modules/PageThumbs.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ProcessHangMonitor", - "resource:///modules/ProcessHangMonitor.jsm"); - #ifdef MOZ_SAFE_BROWSING XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", "resource://gre/modules/SafeBrowsing.jsm"); diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 8afeecd913ba..a42f415bf87b 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -334,26 +334,6 @@ orient="horizontal" hidden="true"/> - - - - - - - diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index a4e2fc761b57..45aa791b8107 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -59,9 +59,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader", XPCOMUtils.defineLazyModuleGetter(this, "PdfJs", "resource://pdf.js/PdfJs.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ProcessHangMonitor", - "resource:///modules/ProcessHangMonitor.jsm"); - #ifdef NIGHTLY_BUILD XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils", "resource://shumway/ShumwayUtils.jsm"); @@ -763,8 +760,6 @@ BrowserGlue.prototype = { } #endif - ProcessHangMonitor.init(); - // A channel for "remote troubleshooting" code... let channel = new WebChannel("remote-troubleshooting", "remote-troubleshooting"); channel.listen((id, data, target) => { diff --git a/browser/devtools/framework/gDevTools.jsm b/browser/devtools/framework/gDevTools.jsm index 2db3bbfd3a9a..968aefcf2eef 100644 --- a/browser/devtools/framework/gDevTools.jsm +++ b/browser/devtools/framework/gDevTools.jsm @@ -854,9 +854,17 @@ let gDevToolsBrowser = { .getService(Ci.nsISlowScriptDebug); let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); - function slowScriptDebugHandler(aTab, aCallback) { - let target = devtools.TargetFactory.forTab(aTab); + debugService.activationHandler = function(aWindow) { + let chromeWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow) + .QueryInterface(Ci.nsIDOMChromeWindow); + let target = devtools.TargetFactory.forTab(chromeWindow.gBrowser.selectedTab); + let setupFinished = false; gDevTools.showToolbox(target, "jsdebugger").then(toolbox => { let threadClient = toolbox.getCurrentPanel().panelWin.gThreadClient; @@ -866,13 +874,13 @@ let gDevToolsBrowser = { case "paused": // When the debugger is already paused. threadClient.breakOnNext(); - aCallback(); + setupFinished = true; break; case "attached": // When the debugger is already open. threadClient.interrupt(() => { threadClient.breakOnNext(); - aCallback(); + setupFinished = true; }); break; case "resuming": @@ -880,7 +888,7 @@ let gDevToolsBrowser = { threadClient.addOneTimeListener("resumed", () => { threadClient.interrupt(() => { threadClient.breakOnNext(); - aCallback(); + setupFinished = true; }); }); break; @@ -889,20 +897,6 @@ let gDevToolsBrowser = { threadClient.state); } }); - } - - debugService.activationHandler = function(aWindow) { - let chromeWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow) - .QueryInterface(Ci.nsIDOMChromeWindow); - - let setupFinished = false; - slowScriptDebugHandler(chromeWindow.gBrowser.selectedTab, - () => { setupFinished = true; }); // Don't return from the interrupt handler until the debugger is brought // up; no reason to continue executing the slow script. @@ -914,18 +908,6 @@ let gDevToolsBrowser = { } utils.leaveModalState(); }; - - debugService.remoteActivationHandler = function(aBrowser, aCallback) { - let chromeWindow = aBrowser.ownerDocument.defaultView; - let tab = chromeWindow.gBrowser.getTabForBrowser(aBrowser); - chromeWindow.gBrowser.selected = tab; - - function callback() { - aCallback.finishDebuggerStartup(); - } - - slowScriptDebugHandler(tab, callback); - }; }, /** diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 0553b443f669..5ed0a3ea5370 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -831,11 +831,3 @@ just addresses the organization to follow, e.g. "This site is run by " --> - - - - - - - - diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index 77f669108c1d..8e37b09fa73c 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -435,11 +435,6 @@ dataReportingNotification.message = %1$S automatically sends some data to dataReportingNotification.button.label = Choose What I Share dataReportingNotification.button.accessKey = C -# Process hang reporter -processHang.message = A web page is causing %1$S to run slowly. What would you like to do? -processHang.button.label = Options -processHang.button.accessKey = O - # Webapps notification popup webapps.install = Install webapps.install.accesskey = I diff --git a/browser/modules/ProcessHangMonitor.jsm b/browser/modules/ProcessHangMonitor.jsm deleted file mode 100644 index 6213e11febdb..000000000000 --- a/browser/modules/ProcessHangMonitor.jsm +++ /dev/null @@ -1,309 +0,0 @@ -/* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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/. */ - -"use strict"; - -let Cc = Components.classes; -let Ci = Components.interfaces; -let Cu = Components.utils; - -this.EXPORTED_SYMBOLS = ["ProcessHangMonitor"]; - -Cu.import("resource://gre/modules/Services.jsm"); - -/** - * This JSM is responsible for observing content process hang reports - * and asking the user what to do about them. See nsIHangReport for - * the platform interface. - */ - -/** - * If a hang hasn't been reported for more than 5 seconds, assume the - * content process has gotten unstuck (and hide the hang notification). - */ -const HANG_EXPIRATION_TIME = 5000; - -let ProcessHangMonitor = { - /** - * Collection of hang reports that haven't expired or been dismissed - * by the user. The keys are nsIHangReports and values keys are - * timers. Each time the hang is reported, the timer is refreshed so - * it expires after HANG_EXPIRATION_TIME. - */ - _activeReports: new Map(), - - /** - * Initialize hang reporting. Called once in the parent process. - */ - init: function() { - Services.obs.addObserver(this, "process-hang-report", false); - Services.obs.addObserver(this, "xpcom-shutdown", false); - Services.ww.registerNotification(this); - }, - - /** - * Terminate JavaScript associated with the hang being reported for - * the selected browser in |win|. - */ - terminateScript: function(win) { - this.handleUserInput(win, report => report.terminateScript()); - }, - - /** - * Start devtools debugger for JavaScript associated with the hang - * being reported for the selected browser in |win|. - */ - debugScript: function(win) { - this.handleUserInput(win, report => { - function callback() { - report.endStartingDebugger(); - } - - report.beginStartingDebugger(); - - let svc = Cc["@mozilla.org/dom/slow-script-debug;1"].getService(Ci.nsISlowScriptDebug); - let handler = svc.remoteActivationHandler; - handler.handleSlowScriptDebug(report.scriptBrowser, callback); - }); - }, - - /** - * Kill the plugin process causing the hang being reported for the - * selected browser in |win|. - */ - terminatePlugin: function(win) { - this.handleUserInput(win, report => report.terminatePlugin()); - }, - - /** - * Kill the content process causing the hang being reported for the selected - * browser in |win|. - */ - terminateProcess: function(win) { - this.handleUserInput(win, report => report.terminateProcess()); - }, - - /** - * Update the "Options" pop-up menu for the hang notification - * associated with the selected browser in |win|. The menu should - * display only options that are relevant to the given report. - */ - refreshMenu: function(win) { - let report = this.findReport(win.gBrowser.selectedBrowser); - if (!report) { - return; - } - - function setVisible(id, visible) { - let item = win.document.getElementById(id); - item.hidden = !visible; - } - - if (report.hangType == report.SLOW_SCRIPT) { - setVisible("processHangTerminateScript", true); - setVisible("processHangDebugScript", true); - setVisible("processHangTerminatePlugin", false); - } else if (report.hangType == report.PLUGIN_HANG) { - setVisible("processHangTerminateScript", false); - setVisible("processHangDebugScript", false); - setVisible("processHangTerminatePlugin", true); - } - }, - - /** - * If there is a hang report associated with the selected browser in - * |win|, invoke |func| on that report and stop notifying the user - * about it. - */ - handleUserInput: function(win, func) { - let report = this.findReport(win.gBrowser.selectedBrowser); - if (!report) { - return; - } - this.removeReport(report); - - return func(report); - }, - - observe: function(subject, topic, data) { - switch (topic) { - case "xpcom-shutdown": - Services.obs.removeObserver(this, "xpcom-shutdown"); - Services.obs.removeObserver(this, "process-hang-report"); - Services.ww.unregisterNotification(this); - break; - - case "process-hang-report": - this.reportHang(subject.QueryInterface(Ci.nsIHangReport)); - break; - - case "domwindowopened": - // Install event listeners on the new window in case one of - // its tabs is already hung. - let win = subject.QueryInterface(Ci.nsIDOMWindow); - let listener = (ev) => { - win.removeEventListener("load", listener, true); - this.updateWindows(); - }; - win.addEventListener("load", listener, true); - break; - } - }, - - /** - * Find any active hang reports for the given element. - */ - findReport: function(browser) { - let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; - for (let [report, timer] of this._activeReports) { - if (report.isReportForBrowser(frameLoader)) { - return report; - } - } - return null; - }, - - /** - * Iterate over all XUL windows and ensure that the proper hang - * reports are shown for each one. Also install event handlers in - * each window to watch for events that would cause a different hang - * report to be displayed. - */ - updateWindows: function() { - let e = Services.wm.getEnumerator("navigator:browser"); - while (e.hasMoreElements()) { - let win = e.getNext(); - - this.updateWindow(win); - - // Only listen for these events if there are active hang reports. - if (this._activeReports.size) { - this.trackWindow(win); - } else { - this.untrackWindow(win); - } - } - }, - - /** - * If there is a hang report for the current tab in |win|, display it. - */ - updateWindow: function(win) { - let report = this.findReport(win.gBrowser.selectedBrowser); - - if (report) { - this.showNotification(win, report); - } else { - this.hideNotification(win); - } - }, - - /** - * Show the notification for a hang. - */ - showNotification: function(win, report) { - let nb = win.document.getElementById("high-priority-global-notificationbox"); - let notification = nb.getNotificationWithValue("process-hang"); - if (notification) { - return; - } - - let bundle = win.gNavigatorBundle; - let brandBundle = win.document.getElementById("bundle_brand"); - let appName = brandBundle.getString("brandShortName"); - let message = bundle.getFormattedString( - "processHang.message", - [appName]); - - let buttons = [{ - label: bundle.getString("processHang.button.label"), - accessKey: bundle.getString("processHang.button.accessKey"), - popup: "processHangOptions", - callback: null, - }]; - - nb.appendNotification(message, "process-hang", - "chrome://browser/content/aboutRobots-icon.png", - nb.PRIORITY_WARNING_HIGH, buttons); - }, - - /** - * Ensure that no hang notifications are visible in |win|. - */ - hideNotification: function(win) { - let nb = win.document.getElementById("high-priority-global-notificationbox"); - let notification = nb.getNotificationWithValue("process-hang"); - if (notification) { - nb.removeNotification(notification); - } - }, - - /** - * Install event handlers on |win| to watch for events that would - * cause a different hang report to be displayed. - */ - trackWindow: function(win) { - win.gBrowser.tabContainer.addEventListener("TabSelect", this, true); - win.gBrowser.tabContainer.addEventListener("TabRemotenessChange", this, true); - }, - - untrackWindow: function(win) { - win.gBrowser.tabContainer.removeEventListener("TabSelect", this, true); - win.gBrowser.tabContainer.removeEventListener("TabRemotenessChange", this, true); - }, - - handleEvent: function(event) { - let win = event.target.ownerDocument.defaultView; - - // If a new tab is selected or if a tab changes remoteness, then - // we may need to show or hide a hang notification. - - if (event.type == "TabSelect" || event.type == "TabRemotenessChange") { - this.updateWindow(win); - } - }, - - /** - * Handle a potentially new hang report. If it hasn't been seen - * before, show a notification for it in all open XUL windows. - */ - reportHang: function(report) { - // If this hang was already reported, then reset the timer for it. - if (this._activeReports.has(report)) { - let timer = this._activeReports.get(report); - timer.cancel(); - timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT); - return; - } - - // Otherwise create a new timer and display the report. - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT); - - this._activeReports.set(report, timer); - this.updateWindows(); - }, - - /** - * Dismiss a hang report because the user closed the notification - * for it or the report expired. - */ - removeReport: function(report) { - this._activeReports.delete(report); - this.updateWindows(); - }, - - /** - * Callback for when HANG_EXPIRATION_TIME has elapsed. - */ - notify: function(timer) { - for (let [otherReport, otherTimer] of this._activeReports) { - if (otherTimer === timer) { - this.removeReport(otherReport); - break; - } - } - }, -}; diff --git a/browser/modules/moz.build b/browser/modules/moz.build index 96501ed48baa..74d81611c9b9 100644 --- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -27,7 +27,6 @@ EXTRA_JS_MODULES += [ 'NetworkPrioritizer.jsm', 'offlineAppCache.jsm', 'PanelFrame.jsm', - 'ProcessHangMonitor.jsm', 'RemotePrompt.jsm', 'SitePermissions.jsm', 'Social.jsm', diff --git a/dom/base/SlowScriptDebug.js b/dom/base/SlowScriptDebug.js index 296889d7b909..c9603abe2aee 100644 --- a/dom/base/SlowScriptDebug.js +++ b/dom/base/SlowScriptDebug.js @@ -16,9 +16,6 @@ SlowScriptDebug.prototype = { get activationHandler() { return this._activationHandler; }, set activationHandler(cb) { return this._activationHandler = cb; }, - - get remoteActivationHandler() { return this._remoteActivationHandler; }, - set remoteActivationHandler(cb) { return this._remoteActivationHandler = cb; }, }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SlowScriptDebug]); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 1daf5fb553cb..07735ae5e6f2 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -67,7 +67,6 @@ #include "mozilla/EventListenerManager.h" #include "mozilla/EventStates.h" #include "mozilla/MouseEvents.h" -#include "mozilla/ProcessHangMonitor.h" #include "AudioChannelService.h" #include "MessageEvent.h" #include "nsAboutProtocolUtils.h" @@ -10970,45 +10969,17 @@ nsGlobalWindow::ShowSlowScriptDialog() return KillSlowScript; } - // Check if we should offer the option to debug - JS::AutoFilename filename; - unsigned lineno; - bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno); - - if (XRE_GetProcessType() == GeckoProcessType_Content && - ProcessHangMonitor::Get()) { - ProcessHangMonitor::SlowScriptAction action; - nsRefPtr monitor = ProcessHangMonitor::Get(); - nsCOMPtr child = do_GetInterface(GetDocShell()); - action = monitor->NotifySlowScript(child, - filename.get(), - lineno); - if (action == ProcessHangMonitor::Terminate) { - return KillSlowScript; - } - - if (action == ProcessHangMonitor::StartDebugger) { - // Spin a nested event loop so that the debugger in the parent can fetch - // any information it needs. Once the debugger has started, return to the - // script. - nsRefPtr outer = GetOuterWindowInternal(); - outer->EnterModalState(); - while (!monitor->IsDebuggerStartupComplete()) { - NS_ProcessNextEvent(nullptr, true); - } - outer->LeaveModalState(); - return ContinueSlowScript; - } - - return ContinueSlowScriptAndKeepNotifying; - } - // Get the nsIPrompt interface from the docshell nsCOMPtr ds = GetDocShell(); NS_ENSURE_TRUE(ds, KillSlowScript); nsCOMPtr prompt = do_GetInterface(ds); NS_ENSURE_TRUE(prompt, KillSlowScript); + // Check if we should offer the option to debug + JS::AutoFilename filename; + unsigned lineno; + bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno); + // Prioritize the SlowScriptDebug interface over JSD1. nsCOMPtr debugCallback; diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 731b1390887c..c9cd5bc49119 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -726,7 +726,6 @@ public: enum SlowScriptResponse { ContinueSlowScript = 0, - ContinueSlowScriptAndKeepNotifying, AlwaysContinueSlowScript, KillSlowScript }; diff --git a/dom/base/nsISlowScriptDebug.idl b/dom/base/nsISlowScriptDebug.idl index 2248b69cac6a..91e91086f332 100644 --- a/dom/base/nsISlowScriptDebug.idl +++ b/dom/base/nsISlowScriptDebug.idl @@ -5,7 +5,6 @@ #include "nsISupports.idl" interface nsIDOMWindow; -interface nsIDOMEventTarget; [scriptable, function, uuid(f7dbb80c-5d1e-4fd9-b55c-a9ffda4a75b1)] interface nsISlowScriptDebugCallback : nsISupports @@ -13,22 +12,8 @@ interface nsISlowScriptDebugCallback : nsISupports void handleSlowScriptDebug(in nsIDOMWindow aWindow); }; -[scriptable, function, uuid(b1c6ecd0-8fa4-11e4-b4a9-0800200c9a66)] -interface nsISlowScriptDebugerStartupCallback : nsISupports -{ - void finishDebuggerStartup(); -}; - -[scriptable, function, uuid(dbee14b0-8fa0-11e4-b4a9-0800200c9a66)] -interface nsISlowScriptDebugRemoteCallback : nsISupports -{ - void handleSlowScriptDebug(in nsIDOMEventTarget aBrowser, - in nsISlowScriptDebugerStartupCallback aCallback); -}; - [scriptable, uuid(f75d4164-3aa7-4395-ba44-a5f95b2e8427)] interface nsISlowScriptDebug : nsISupports { attribute nsISlowScriptDebugCallback activationHandler; - attribute nsISlowScriptDebugRemoteCallback remoteActivationHandler; }; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 4df3de573776..4c8f1048b931 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -24,7 +24,6 @@ #include "mozilla/a11y/DocAccessibleChild.h" #endif #include "mozilla/Preferences.h" -#include "mozilla/ProcessHangMonitorIPC.h" #include "mozilla/docshell/OfflineCacheUpdateChild.h" #include "mozilla/dom/ContentBridgeChild.h" #include "mozilla/dom/ContentBridgeParent.h" @@ -1000,13 +999,6 @@ ContentChild::AllocPBackgroundChild(Transport* aTransport, return BackgroundChild::Alloc(aTransport, aOtherProcess); } -PProcessHangMonitorChild* -ContentChild::AllocPProcessHangMonitorChild(Transport* aTransport, - ProcessId aOtherProcess) -{ - return CreateHangMonitorChild(aTransport, aOtherProcess); -} - #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) static void SetUpSandboxEnvironment() diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 0e76e01059ca..1e2e8a37ddc3 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -126,10 +126,6 @@ public: AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) MOZ_OVERRIDE; - PProcessHangMonitorChild* - AllocPProcessHangMonitorChild(Transport* aTransport, - ProcessId aOtherProcess) MOZ_OVERRIDE; - #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) // Cleans up any resources used by the process when sandboxed. void CleanUpSandboxEnvironment(); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 7ecbf3c5fb33..f7f52a3e3b17 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -73,8 +73,6 @@ #include "mozilla/net/NeckoParent.h" #include "mozilla/plugins/PluginBridge.h" #include "mozilla/Preferences.h" -#include "mozilla/ProcessHangMonitor.h" -#include "mozilla/ProcessHangMonitorIPC.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/Telemetry.h" @@ -1804,11 +1802,6 @@ ContentParent::ActorDestroy(ActorDestroyReason why) // finish waiting in the xpcom-shutdown/profile-before-change observer. mIPCOpen = false; - if (mHangMonitorActor) { - ProcessHangMonitor::RemoveProcess(mHangMonitorActor); - mHangMonitorActor = nullptr; - } - if (why == NormalShutdown && !mCalledClose) { // If we shut down normally but haven't called Close, assume somebody // else called Close on us. In that case, we still need to call @@ -2041,7 +2034,6 @@ ContentParent::InitializeMembers() mCreatedPairedMinidumps = false; mShutdownPending = false; mIPCOpen = true; - mHangMonitorActor = nullptr; } ContentParent::ContentParent(mozIApplication* aApp, @@ -2121,8 +2113,6 @@ ContentParent::ContentParent(mozIApplication* aApp, ContentProcessManager::GetSingleton()->AddContentProcess(this); - ProcessHangMonitor::AddProcess(this); - // Set a reply timeout for CPOWs. SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0)); } @@ -3060,14 +3050,6 @@ ContentParent::AllocPBackgroundParent(Transport* aTransport, return BackgroundParent::Alloc(this, aTransport, aOtherProcess); } -PProcessHangMonitorParent* -ContentParent::AllocPProcessHangMonitorParent(Transport* aTransport, - ProcessId aOtherProcess) -{ - mHangMonitorActor = CreateHangMonitorParent(this, aTransport, aOtherProcess); - return mHangMonitorActor; -} - PSharedBufferManagerParent* ContentParent::AllocPSharedBufferManagerParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) @@ -4317,8 +4299,7 @@ ContentParent::RecvNotifyKeywordSearchLoading(const nsString &aProvider, bool ContentParent::ShouldContinueFromReplyTimeout() { - nsRefPtr monitor = ProcessHangMonitor::Get(); - return !monitor || !monitor->ShouldTimeOutCPOWs(); + return false; } bool diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index c2bb0570586a..4ec30c9035a7 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -485,10 +485,6 @@ private: AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess) MOZ_OVERRIDE; - PProcessHangMonitorParent* - AllocPProcessHangMonitorParent(Transport* aTransport, - ProcessId aOtherProcess) MOZ_OVERRIDE; - virtual bool RecvGetProcessAttributes(ContentParentId* aCpId, bool* aIsForApp, bool* aIsForBrowser) MOZ_OVERRIDE; @@ -843,8 +839,6 @@ private: static int32_t sNuwaPid; static bool sNuwaReady; #endif - - PProcessHangMonitorParent* mHangMonitorActor; }; } // namespace dom diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 772c22be6a30..4d32082bdec2 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -21,7 +21,6 @@ include protocol PFileDescriptorSet; include protocol PFMRadio; include protocol PFileSystemRequest; include protocol PHal; -include protocol PProcessHangMonitor; include protocol PImageBridge; include protocol PMemoryReportRequest; include protocol PMobileConnection; @@ -346,7 +345,6 @@ prio(normal upto urgent) intr protocol PContent parent spawns PPluginModule; parent opens PCompositor; - parent opens PProcessHangMonitor; parent opens PSharedBufferManager; parent opens PImageBridge; child opens PBackground; diff --git a/dom/ipc/PProcessHangMonitor.ipdl b/dom/ipc/PProcessHangMonitor.ipdl deleted file mode 100644 index 9ac0115a15be..000000000000 --- a/dom/ipc/PProcessHangMonitor.ipdl +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: sw=2 ts=8 et : - */ -/* 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/. */ - -using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; - -namespace mozilla { - -struct SlowScriptData -{ - TabId tabId; - nsCString filename; - uint32_t lineno; -}; - -struct PluginHangData -{ - uint32_t pluginId; -}; - -union HangData -{ - SlowScriptData; - PluginHangData; -}; - -protocol PProcessHangMonitor -{ -parent: - async HangEvidence(HangData data); - -child: - async TerminateScript(); - - async BeginStartingDebugger(); - async EndStartingDebugger(); -}; - -} // namespace mozilla diff --git a/dom/ipc/ProcessHangMonitor.cpp b/dom/ipc/ProcessHangMonitor.cpp deleted file mode 100644 index 75a5df9d3cc7..000000000000 --- a/dom/ipc/ProcessHangMonitor.cpp +++ /dev/null @@ -1,954 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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 "mozilla/ProcessHangMonitor.h" -#include "mozilla/ProcessHangMonitorIPC.h" - -#include "mozilla/Atomics.h" -#include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/Element.h" -#include "mozilla/dom/TabChild.h" -#include "mozilla/dom/TabParent.h" -#include "mozilla/Monitor.h" -#include "mozilla/plugins/PluginBridge.h" -#include "mozilla/Preferences.h" -#include "mozilla/unused.h" - -#include "nsIFrameLoader.h" -#include "nsIHangReport.h" -#include "nsITabParent.h" -#include "nsPluginHost.h" -#include "nsThreadUtils.h" - -#include "base/task.h" -#include "base/thread.h" - -using namespace mozilla; -using namespace mozilla::dom; - -/* - * Basic architecture: - * - * Each process has its own ProcessHangMonitor singleton. This singleton exists - * as long as there is at least one content process in the system. Each content - * process has a HangMonitorChild and the chrome process has one - * HangMonitorParent per process. Each process (including the chrome process) - * runs a hang monitoring thread. The PHangMonitor actors are bound to this - * thread so that they never block on the main thread. - * - * When the content process detects a hang, it posts a task to its hang thread, - * which sends an IPC message to the hang thread in the parent. The parent - * cancels any ongoing CPOW requests and then posts a runnable to the main - * thread that notifies Firefox frontend code of the hang. The frontend code is - * passed an nsIHangReport, which can be used to terminate the hang. - * - * If the user chooses to terminate a script, a task is posted to the chrome - * process's hang monitoring thread, which sends an IPC message to the hang - * thread in the content process. That thread sets a flag to indicate that JS - * execution should be terminated the next time it hits the interrupt - * callback. A similar scheme is used for debugging slow scripts. If a content - * process or plug-in needs to be terminated, the chrome process does so - * directly, without messaging the content process. - */ - -namespace { - -/* Child process objects */ - -class HangMonitorChild - : public PProcessHangMonitorChild -{ - public: - explicit HangMonitorChild(ProcessHangMonitor* aMonitor); - virtual ~HangMonitorChild(); - - void Open(Transport* aTransport, ProcessHandle aHandle, - MessageLoop* aIOLoop); - - typedef ProcessHangMonitor::SlowScriptAction SlowScriptAction; - SlowScriptAction NotifySlowScript(nsITabChild* aTabChild, - const char* aFileName, - unsigned aLineNo); - void NotifySlowScriptAsync(TabId aTabId, - const nsCString& aFileName, - unsigned aLineNo); - - bool IsDebuggerStartupComplete(); - - void NotifyPluginHang(uint32_t aPluginId); - void NotifyPluginHangAsync(uint32_t aPluginId); - - void ClearHang(); - - virtual bool RecvTerminateScript() MOZ_OVERRIDE; - virtual bool RecvBeginStartingDebugger() MOZ_OVERRIDE; - virtual bool RecvEndStartingDebugger() MOZ_OVERRIDE; - - virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - void Shutdown(); - - static HangMonitorChild* Get() { return sInstance; } - - MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); } - - private: - void ShutdownOnThread(); - - static HangMonitorChild* sInstance; - - const nsRefPtr mHangMonitor; - Monitor mMonitor; - - // Main thread-only. - bool mSentReport; - - // These fields must be accessed with mMonitor held. - bool mTerminateScript; - bool mStartDebugger; - bool mFinishedStartingDebugger; - bool mShutdownDone; - - // This field is only accessed on the hang thread. - bool mIPCOpen; -}; - -HangMonitorChild* HangMonitorChild::sInstance; - -/* Parent process objects */ - -class HangMonitorParent; - -class HangMonitoredProcess MOZ_FINAL - : public nsIHangReport -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - - HangMonitoredProcess(HangMonitorParent* aActor, - ContentParent* aContentParent) - : mActor(aActor), mContentParent(aContentParent) {} - - NS_IMETHOD GetHangType(uint32_t* aHangType) MOZ_OVERRIDE; - NS_IMETHOD GetScriptBrowser(nsIDOMElement** aBrowser) MOZ_OVERRIDE; - NS_IMETHOD GetScriptFileName(nsACString& aFileName) MOZ_OVERRIDE; - NS_IMETHOD GetScriptLineNo(uint32_t* aLineNo) MOZ_OVERRIDE; - - NS_IMETHOD GetPluginName(nsACString& aPluginName) MOZ_OVERRIDE; - - NS_IMETHOD TerminateScript() MOZ_OVERRIDE; - NS_IMETHOD BeginStartingDebugger() MOZ_OVERRIDE; - NS_IMETHOD EndStartingDebugger() MOZ_OVERRIDE; - NS_IMETHOD TerminatePlugin() MOZ_OVERRIDE; - NS_IMETHOD TerminateProcess() MOZ_OVERRIDE; - - NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult); - - void Clear() { mContentParent = nullptr; mActor = nullptr; } - - void SetHangData(const HangData& aHangData) { mHangData = aHangData; } - -private: - ~HangMonitoredProcess() {} - - // Everything here is main thread-only. - HangMonitorParent* mActor; - ContentParent* mContentParent; - HangData mHangData; -}; - -class HangMonitorParent - : public PProcessHangMonitorParent -{ -public: - explicit HangMonitorParent(ProcessHangMonitor* aMonitor); - virtual ~HangMonitorParent(); - - void Open(Transport* aTransport, ProcessHandle aHandle, - MessageLoop* aIOLoop); - - virtual bool RecvHangEvidence(const HangData& aHangData) MOZ_OVERRIDE; - - virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; } - - void Shutdown(); - - void TerminateScript(); - void BeginStartingDebugger(); - void EndStartingDebugger(); - - MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); } - - private: - void ShutdownOnThread(); - - const nsRefPtr mHangMonitor; - - // This field is read-only after construction. - bool mReportHangs; - - // This field is only accessed on the hang thread. - bool mIPCOpen; - - Monitor mMonitor; - - // Must be accessed with mMonitor held. - nsRefPtr mProcess; - bool mShutdownDone; -}; - -} // namespace - -template<> -struct RunnableMethodTraits -{ - typedef HangMonitorChild Class; - static void RetainCallee(Class* obj) { } - static void ReleaseCallee(Class* obj) { } -}; - -template<> -struct RunnableMethodTraits -{ - typedef HangMonitorParent Class; - static void RetainCallee(Class* obj) { } - static void ReleaseCallee(Class* obj) { } -}; - -/* HangMonitorChild implementation */ - -HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor) - : mHangMonitor(aMonitor), - mMonitor("HangMonitorChild lock"), - mSentReport(false), - mTerminateScript(false), - mStartDebugger(false), - mFinishedStartingDebugger(false), - mShutdownDone(false), - mIPCOpen(true) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); -} - -HangMonitorChild::~HangMonitorChild() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(sInstance == this); - sInstance = nullptr; -} - -void -HangMonitorChild::Shutdown() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - MonitorAutoLock lock(mMonitor); - while (!mShutdownDone) { - mMonitor.Wait(); - } -} - -void -HangMonitorChild::ShutdownOnThread() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - MonitorAutoLock lock(mMonitor); - mShutdownDone = true; - mMonitor.Notify(); -} - -void -HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy) -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - mIPCOpen = false; - - // We use a task here to ensure that IPDL is finished with this - // HangMonitorChild before it gets deleted on the main thread. - MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &HangMonitorChild::ShutdownOnThread)); -} - -bool -HangMonitorChild::RecvTerminateScript() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - MonitorAutoLock lock(mMonitor); - mTerminateScript = true; - return true; -} - -bool -HangMonitorChild::RecvBeginStartingDebugger() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - MonitorAutoLock lock(mMonitor); - mStartDebugger = true; - return true; -} - -bool -HangMonitorChild::RecvEndStartingDebugger() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - MonitorAutoLock lock(mMonitor); - mFinishedStartingDebugger = true; - return true; -} - -void -HangMonitorChild::Open(Transport* aTransport, ProcessHandle aHandle, - MessageLoop* aIOLoop) -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - MOZ_ASSERT(!sInstance); - sInstance = this; - - DebugOnly ok = PProcessHangMonitorChild::Open(aTransport, aHandle, aIOLoop); - MOZ_ASSERT(ok); -} - -void -HangMonitorChild::NotifySlowScriptAsync(TabId aTabId, - const nsCString& aFileName, - unsigned aLineNo) -{ - if (mIPCOpen) { - unused << SendHangEvidence(SlowScriptData(aTabId, aFileName, aLineNo)); - } -} - -HangMonitorChild::SlowScriptAction -HangMonitorChild::NotifySlowScript(nsITabChild* aTabChild, - const char* aFileName, - unsigned aLineNo) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - mSentReport = true; - - { - MonitorAutoLock lock(mMonitor); - - if (mTerminateScript) { - mTerminateScript = false; - return SlowScriptAction::Terminate; - } - - if (mStartDebugger) { - mStartDebugger = false; - return SlowScriptAction::StartDebugger; - } - } - - TabId id; - if (aTabChild) { - nsRefPtr tabChild = static_cast(aTabChild); - id = tabChild->GetTabId(); - } - nsAutoCString filename(aFileName); - - MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &HangMonitorChild::NotifySlowScriptAsync, - id, filename, aLineNo)); - return SlowScriptAction::Continue; -} - -bool -HangMonitorChild::IsDebuggerStartupComplete() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - MonitorAutoLock lock(mMonitor); - - if (mFinishedStartingDebugger) { - mFinishedStartingDebugger = false; - return true; - } - - return false; -} - -void -HangMonitorChild::NotifyPluginHang(uint32_t aPluginId) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - mSentReport = true; - - MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, - &HangMonitorChild::NotifyPluginHangAsync, - aPluginId)); -} - -void -HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId) -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - if (mIPCOpen) { - unused << SendHangEvidence(PluginHangData(aPluginId)); - } -} - -void -HangMonitorChild::ClearHang() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mSentReport) { - MonitorAutoLock lock(mMonitor); - mSentReport = false; - mTerminateScript = false; - mStartDebugger = false; - mFinishedStartingDebugger = false; - } -} - -/* HangMonitorParent implementation */ - -HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor) - : mHangMonitor(aMonitor), - mIPCOpen(true), - mMonitor("HangMonitorParent lock"), - mShutdownDone(false) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false); -} - -HangMonitorParent::~HangMonitorParent() -{ - // For some reason IPDL doesn't autmatically delete the channel for a - // bridged protocol (bug 1090570). So we have to do it ourselves. - XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask(GetTransport())); -} - -void -HangMonitorParent::Shutdown() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - MonitorAutoLock lock(mMonitor); - - if (mProcess) { - mProcess->Clear(); - mProcess = nullptr; - } - - MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &HangMonitorParent::ShutdownOnThread)); - - while (!mShutdownDone) { - mMonitor.Wait(); - } -} - -void -HangMonitorParent::ShutdownOnThread() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - // mIPCOpen is only written from this thread, so need need to take the lock - // here. We'd be shooting ourselves in the foot, because ActorDestroy takes - // it. - if (mIPCOpen) { - Close(); - } - - MonitorAutoLock lock(mMonitor); - mShutdownDone = true; - mMonitor.Notify(); -} - -void -HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy) -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - mIPCOpen = false; -} - -void -HangMonitorParent::Open(Transport* aTransport, ProcessHandle aHandle, - MessageLoop* aIOLoop) -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - DebugOnly ok = PProcessHangMonitorParent::Open(aTransport, aHandle, aIOLoop); - MOZ_ASSERT(ok); -} - -class HangObserverNotifier MOZ_FINAL : public nsRunnable -{ -public: - HangObserverNotifier(HangMonitoredProcess* aProcess, const HangData& aHangData) - : mProcess(aProcess), - mHangData(aHangData) - {} - - NS_IMETHOD - Run() - { - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - mProcess->SetHangData(mHangData); - - nsCOMPtr observerService = - mozilla::services::GetObserverService(); - observerService->NotifyObservers(mProcess, "process-hang-report", nullptr); - return NS_OK; - } - -private: - nsRefPtr mProcess; - HangData mHangData; -}; - -bool -HangMonitorParent::RecvHangEvidence(const HangData& aHangData) -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - if (!mReportHangs) { - return true; - } - - mHangMonitor->InitiateCPOWTimeout(); - - MonitorAutoLock lock(mMonitor); - - nsCOMPtr notifier = new HangObserverNotifier(mProcess, aHangData); - NS_DispatchToMainThread(notifier); - - return true; -} - -void -HangMonitorParent::TerminateScript() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - if (mIPCOpen) { - unused << SendTerminateScript(); - } -} - -void -HangMonitorParent::BeginStartingDebugger() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - if (mIPCOpen) { - unused << SendBeginStartingDebugger(); - } -} - -void -HangMonitorParent::EndStartingDebugger() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - - if (mIPCOpen) { - unused << SendEndStartingDebugger(); - } -} - -/* HangMonitoredProcess implementation */ - -NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport) - -NS_IMETHODIMP -HangMonitoredProcess::GetHangType(uint32_t* aHangType) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - switch (mHangData.type()) { - case HangData::TSlowScriptData: - *aHangType = SLOW_SCRIPT; - break; - case HangData::TPluginHangData: - *aHangType = PLUGIN_HANG; - break; - default: - MOZ_ASSERT(false); - return NS_ERROR_UNEXPECTED; - break; - } - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::GetScriptBrowser(nsIDOMElement** aBrowser) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (mHangData.type() != HangData::TSlowScriptData) { - return NS_ERROR_NOT_AVAILABLE; - } - - TabId tabId = mHangData.get_SlowScriptData().tabId(); - if (!mContentParent) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsTArray tabs; - mContentParent->ManagedPBrowserParent(tabs); - for (size_t i = 0; i < tabs.Length(); i++) { - TabParent* tp = static_cast(tabs[i]); - if (tp->GetTabId() == tabId) { - nsCOMPtr node = do_QueryInterface(tp->GetOwnerElement()); - node.forget(aBrowser); - return NS_OK; - } - } - - *aBrowser = nullptr; - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::GetScriptFileName(nsACString& aFileName) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (mHangData.type() != HangData::TSlowScriptData) { - return NS_ERROR_NOT_AVAILABLE; - } - - aFileName = mHangData.get_SlowScriptData().filename(); - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::GetScriptLineNo(uint32_t* aLineNo) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (mHangData.type() != HangData::TSlowScriptData) { - return NS_ERROR_NOT_AVAILABLE; - } - - *aLineNo = mHangData.get_SlowScriptData().lineno(); - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::GetPluginName(nsACString& aPluginName) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (mHangData.type() != HangData::TPluginHangData) { - return NS_ERROR_NOT_AVAILABLE; - } - - uint32_t id = mHangData.get_PluginHangData().pluginId(); - - nsRefPtr host = nsPluginHost::GetInst(); - nsPluginTag* tag = host->PluginWithId(id); - if (!tag) { - return NS_ERROR_UNEXPECTED; - } - - aPluginName = tag->mName; - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::TerminateScript() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (mHangData.type() != HangData::TSlowScriptData) { - return NS_ERROR_UNEXPECTED; - } - - if (!mActor) { - return NS_ERROR_UNEXPECTED; - } - - ProcessHangMonitor::Get()->MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(mActor, &HangMonitorParent::TerminateScript)); - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::BeginStartingDebugger() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (mHangData.type() != HangData::TSlowScriptData) { - return NS_ERROR_UNEXPECTED; - } - - if (!mActor) { - return NS_ERROR_UNEXPECTED; - } - - ProcessHangMonitor::Get()->MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(mActor, &HangMonitorParent::BeginStartingDebugger)); - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::EndStartingDebugger() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (mHangData.type() != HangData::TSlowScriptData) { - return NS_ERROR_UNEXPECTED; - } - - if (!mActor) { - return NS_ERROR_UNEXPECTED; - } - - ProcessHangMonitor::Get()->MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(mActor, &HangMonitorParent::EndStartingDebugger)); - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::TerminatePlugin() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (mHangData.type() != HangData::TPluginHangData) { - return NS_ERROR_UNEXPECTED; - } - - uint32_t id = mHangData.get_PluginHangData().pluginId(); - plugins::TerminatePlugin(id); - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::TerminateProcess() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - if (!mContentParent) { - return NS_ERROR_UNEXPECTED; - } - - mContentParent->KillHard(); - return NS_OK; -} - -NS_IMETHODIMP -HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - if (!mActor) { - *aResult = false; - return NS_OK; - } - - nsCOMPtr itp; - aFrameLoader->GetTabParent(getter_AddRefs(itp)); - if (!itp) { - *aResult = false; - return NS_OK; - } - - *aResult = mContentParent == static_cast(itp.get())->Manager(); - return NS_OK; -} - -ProcessHangMonitor* ProcessHangMonitor::sInstance; - -ProcessHangMonitor::ProcessHangMonitor() - : mCPOWTimeout(false) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - MOZ_COUNT_CTOR(ProcessHangMonitor); - - if (XRE_GetProcessType() == GeckoProcessType_Content) { - nsCOMPtr obs = mozilla::services::GetObserverService(); - obs->AddObserver(this, "xpcom-shutdown", false); - } - - mThread = new base::Thread("ProcessHangMonitor"); - if (!mThread->Start()) { - delete mThread; - mThread = nullptr; - } -} - -ProcessHangMonitor::~ProcessHangMonitor() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - MOZ_COUNT_DTOR(ProcessHangMonitor); - - MOZ_ASSERT(sInstance == this); - sInstance = nullptr; - - delete mThread; -} - -ProcessHangMonitor* -ProcessHangMonitor::GetOrCreate() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (!sInstance) { - sInstance = new ProcessHangMonitor(); - } - return sInstance; -} - -NS_IMPL_ISUPPORTS(ProcessHangMonitor, nsIObserver) - -NS_IMETHODIMP -ProcessHangMonitor::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (!strcmp(aTopic, "xpcom-shutdown")) { - if (HangMonitorChild* child = HangMonitorChild::Get()) { - child->Shutdown(); - delete child; - } - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, "xpcom-shutdown"); - } - } - return NS_OK; -} - -ProcessHangMonitor::SlowScriptAction -ProcessHangMonitor::NotifySlowScript(nsITabChild* aTabChild, - const char* aFileName, - unsigned aLineNo) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - return HangMonitorChild::Get()->NotifySlowScript(aTabChild, aFileName, aLineNo); -} - -bool -ProcessHangMonitor::IsDebuggerStartupComplete() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - return HangMonitorChild::Get()->IsDebuggerStartupComplete(); -} - -bool -ProcessHangMonitor::ShouldTimeOutCPOWs() -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - if (mCPOWTimeout) { - mCPOWTimeout = false; - return true; - } - return false; -} - -void -ProcessHangMonitor::InitiateCPOWTimeout() -{ - MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); - mCPOWTimeout = true; -} - -void -ProcessHangMonitor::NotifyPluginHang(uint32_t aPluginId) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - return HangMonitorChild::Get()->NotifyPluginHang(aPluginId); -} - -PProcessHangMonitorParent* -mozilla::CreateHangMonitorParent(ContentParent* aContentParent, - mozilla::ipc::Transport* aTransport, - base::ProcessId aOtherProcess) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate(); - HangMonitorParent* parent = new HangMonitorParent(monitor); - - HangMonitoredProcess* process = new HangMonitoredProcess(parent, aContentParent); - parent->SetProcess(process); - - base::ProcessHandle handle; - if (!base::OpenProcessHandle(aOtherProcess, &handle)) { - // XXX need to kill |aOtherProcess|, it's boned - return nullptr; - } - - monitor->MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(parent, &HangMonitorParent::Open, - aTransport, handle, XRE_GetIOMessageLoop())); - - return parent; -} - -PProcessHangMonitorChild* -mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport, - base::ProcessId aOtherProcess) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate(); - HangMonitorChild* child = new HangMonitorChild(monitor); - - base::ProcessHandle handle; - if (!base::OpenProcessHandle(aOtherProcess, &handle)) { - // XXX need to kill |aOtherProcess|, it's boned - return nullptr; - } - - monitor->MonitorLoop()->PostTask( - FROM_HERE, - NewRunnableMethod(child, &HangMonitorChild::Open, - aTransport, handle, XRE_GetIOMessageLoop())); - - return child; -} - -MessageLoop* -ProcessHangMonitor::MonitorLoop() -{ - return mThread->message_loop(); -} - -/* static */ void -ProcessHangMonitor::AddProcess(ContentParent* aContentParent) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - if (mozilla::Preferences::GetBool("dom.ipc.processHangMonitor", false)) { - DebugOnly opened = PProcessHangMonitor::Open(aContentParent); - MOZ_ASSERT(opened); - } -} - -/* static */ void -ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent* aParent) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - auto parent = static_cast(aParent); - parent->Shutdown(); - delete parent; -} - -/* static */ void -ProcessHangMonitor::ClearHang() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (HangMonitorChild* child = HangMonitorChild::Get()) { - child->ClearHang(); - } -} diff --git a/dom/ipc/ProcessHangMonitor.h b/dom/ipc/ProcessHangMonitor.h deleted file mode 100644 index 39ef1652d102..000000000000 --- a/dom/ipc/ProcessHangMonitor.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef mozilla_ProcessHangMonitor_h -#define mozilla_ProcessHangMonitor_h - -#include "mozilla/Atomics.h" -#include "nsIObserver.h" - -class nsGlobalWindow; -class nsITabChild; - -class MessageLoop; - -namespace base { -class Thread; -}; - -namespace mozilla { - -namespace dom { -class ContentParent; -} - -class PProcessHangMonitorParent; -class PProcessHangMonitorChild; - -class ProcessHangMonitor MOZ_FINAL - : public nsIObserver -{ - private: - ProcessHangMonitor(); - virtual ~ProcessHangMonitor(); - - public: - static ProcessHangMonitor* Get() { return sInstance; } - static ProcessHangMonitor* GetOrCreate(); - - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - - static void AddProcess(dom::ContentParent* aContentParent); - static void RemoveProcess(PProcessHangMonitorParent* aParent); - - static void ClearHang(); - - enum SlowScriptAction { - Continue, - Terminate, - StartDebugger - }; - SlowScriptAction NotifySlowScript(nsITabChild* aTabChild, - const char* aFileName, - unsigned aLineNo); - - void NotifyPluginHang(uint32_t aPluginId); - - bool IsDebuggerStartupComplete(); - - void InitiateCPOWTimeout(); - bool ShouldTimeOutCPOWs(); - - MessageLoop* MonitorLoop(); - - private: - static ProcessHangMonitor* sInstance; - - Atomic mCPOWTimeout; - - base::Thread* mThread; -}; - -} // namespace mozilla - -#endif // mozilla_ProcessHangMonitor_h diff --git a/dom/ipc/ProcessHangMonitorIPC.h b/dom/ipc/ProcessHangMonitorIPC.h deleted file mode 100644 index 87c0e856bb1f..000000000000 --- a/dom/ipc/ProcessHangMonitorIPC.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef mozilla_ProcessHangMonitorIPC_h -#define mozilla_ProcessHangMonitorIPC_h - -#include "base/task.h" -#include "base/thread.h" - -#include "mozilla/PProcessHangMonitor.h" -#include "mozilla/PProcessHangMonitorParent.h" -#include "mozilla/PProcessHangMonitorChild.h" - -namespace mozilla { - -namespace dom { -class ContentParent; -} - -PProcessHangMonitorParent* -CreateHangMonitorParent(mozilla::dom::ContentParent* aContentParent, - mozilla::ipc::Transport* aTransport, - base::ProcessId aOtherProcess); - -PProcessHangMonitorChild* -CreateHangMonitorChild(mozilla::ipc::Transport* aTransport, - base::ProcessId aOtherProcess); - -} // namespace mozilla - -#endif // mozilla_ProcessHangMonitorIPC_h diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build index ce808549ba78..f8b0427f2fa2 100644 --- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -4,12 +4,6 @@ # 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/. -XPIDL_SOURCES += [ - 'nsIHangReport.idl', -] - -XPIDL_MODULE = 'dom' - EXPORTS += [ 'nsICachedFileDescriptorListener.h', ] @@ -45,8 +39,6 @@ EXPORTS.mozilla.dom += [ EXPORTS.mozilla += [ 'AppProcessChecker.h', 'PreallocatedProcessManager.h', - 'ProcessHangMonitor.h', - 'ProcessHangMonitorIPC.h', 'ProcessPriorityManager.h', ] @@ -81,7 +73,6 @@ SOURCES += [ 'Blob.cpp', 'ContentChild.cpp', 'CrashReporterChild.cpp', - 'ProcessHangMonitor.cpp', ] IPDL_SOURCES += [ @@ -101,7 +92,6 @@ IPDL_SOURCES += [ 'PFilePicker.ipdl', 'PMemoryReportRequest.ipdl', 'PPluginWidget.ipdl', - 'PProcessHangMonitor.ipdl', 'PScreenManager.ipdl', 'PTabContext.ipdlh', ] diff --git a/dom/ipc/nsIHangReport.idl b/dom/ipc/nsIHangReport.idl deleted file mode 100644 index d8ac12505288..000000000000 --- a/dom/ipc/nsIHangReport.idl +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "nsISupports.idl" - -interface nsIDOMElement; -interface nsIFrameLoader; - -/** - * When a content process hangs, Gecko notifies "process-hang-report" observers - * and passes an nsIHangReport for the subject parameter. There is at most one - * nsIHangReport associated with a given content process. As long as the content - * process stays stuck, the "process-hang-report" observer will continue to be - * notified at regular intervals (approximately once per second). The content - * process will continue to run uninhibitedly during this time. - */ - -[scriptable, uuid(3b88d100-8d5b-11e4-b4a9-0800200c9a66)] -interface nsIHangReport : nsISupports -{ - const unsigned long SLOW_SCRIPT = 1; - const unsigned long PLUGIN_HANG = 2; - - // The type of hang being reported: SLOW_SCRIPT or PLUGIN_HANG. - readonly attribute unsigned long hangType; - - // For SLOW_SCRIPT reports, these fields contain information about the - // slow script. - // Only valid for SLOW_SCRIPT reports. - readonly attribute nsIDOMElement scriptBrowser; - readonly attribute ACString scriptFileName; - readonly attribute unsigned long scriptLineNo; - - // For PLUGIN_HANGs, this field contains information about the plugin. - // Only valid for PLUGIN_HANG reports. - readonly attribute ACString pluginName; - - // Terminate the slow script if it is still running. - // Only valid for SLOW_SCRIPT reports. - void terminateScript(); - - // Terminate the plugin if it is still hung. - // Only valid for PLUGIN_HANG reports. - void terminatePlugin(); - - // Terminate the hung content process unconditionally. - // Valid for any type of hang. - void terminateProcess(); - - // Ask the content process to start up the slow script debugger. - // Only valid for SLOW_SCRIPT reports. - void beginStartingDebugger(); - - // Inform the content process that the slow script debugger has finished - // spinning up. The content process will run a nested event loop until this - // method is called. - // Only valid for SLOW_SCRIPT reports. - void endStartingDebugger(); - - // Inquire whether the report is for a content process loaded by the given - // frameloader. - bool isReportForBrowser(in nsIFrameLoader aFrameLoader); -}; diff --git a/dom/plugins/base/nsPluginHost.h b/dom/plugins/base/nsPluginHost.h index 578e653284ba..8124dde36d07 100644 --- a/dom/plugins/base/nsPluginHost.h +++ b/dom/plugins/base/nsPluginHost.h @@ -201,8 +201,6 @@ public: // Does not accept nullptr and should never fail. nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin); - nsPluginTag* PluginWithId(uint32_t aId); - nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin); nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin); void NotifyContentModuleDestroyed(uint32_t aPluginId); @@ -278,6 +276,7 @@ private: // Returns the first plugin at |path| nsPluginTag* FirstPluginWithPath(const nsCString& path); + nsPluginTag* PluginWithId(uint32_t aId); nsresult EnsurePrivateDirServiceProvider(); diff --git a/dom/plugins/ipc/PluginBridge.h b/dom/plugins/ipc/PluginBridge.h index a8abcd43d70a..8bd5963602c1 100644 --- a/dom/plugins/ipc/PluginBridge.h +++ b/dom/plugins/ipc/PluginBridge.h @@ -24,9 +24,6 @@ FindPluginsForContent(uint32_t aPluginEpoch, nsTArray* aPlugins, uint32_t* aNewPluginEpoch); -void -TerminatePlugin(uint32_t aPluginId); - } // namespace plugins } // namespace mozilla diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index 98acf02d893b..4e4562bd4537 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -21,7 +21,6 @@ #include "mozilla/plugins/PluginBridge.h" #include "mozilla/plugins/PluginInstanceParent.h" #include "mozilla/Preferences.h" -#include "mozilla/ProcessHangMonitor.h" #include "mozilla/Services.h" #include "mozilla/Telemetry.h" #include "mozilla/unused.h" @@ -70,7 +69,6 @@ using namespace mozilla::plugins::parent; using namespace CrashReporter; #endif -static const char kContentTimeoutPref[] = "dom.ipc.plugins.contentTimeoutSecs"; static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs"; static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs"; static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs"; @@ -261,22 +259,6 @@ PRCList PluginModuleMapping::sModuleListHead = bool PluginModuleMapping::sIsLoadModuleOnStack = false; -void -mozilla::plugins::TerminatePlugin(uint32_t aPluginId) -{ - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); - - nsRefPtr host = nsPluginHost::GetInst(); - nsPluginTag* pluginTag = host->PluginWithId(aPluginId); - if (!pluginTag || !pluginTag->mPlugin) { - return; - } - - nsRefPtr plugin = pluginTag->mPlugin; - PluginModuleChromeParent* chromeParent = static_cast(plugin->GetLibrary()); - chromeParent->TerminateChildProcess(MessageLoop::current()); -} - /* static */ PluginLibrary* PluginModuleContentParent::LoadModule(uint32_t aPluginId) { @@ -307,8 +289,6 @@ PluginModuleContentParent::LoadModule(uint32_t aPluginId) mapping.forget(); } - parent->mPluginId = aPluginId; - return parent; } @@ -348,8 +328,6 @@ PluginModuleContentParent::Initialize(mozilla::ipc::Transport* aTransport, // all share the same channel. parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION); - TimeoutChanged(kContentTimeoutPref, parent); - // moduleMapping is linked into PluginModuleMapping::sModuleListHead and is // needed later, so since this function is returning successfully we // forget it here. @@ -539,12 +517,6 @@ PluginModuleParent::~PluginModuleParent() PluginModuleContentParent::PluginModuleContentParent() : PluginModuleParent(false) { - Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this); -} - -PluginModuleContentParent::~PluginModuleContentParent() -{ - Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this); } PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId) @@ -678,7 +650,7 @@ PluginModuleChromeParent::WriteExtraDataForMinidump(AnnotationTable& notes) #endif // MOZ_CRASHREPORTER void -PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout) +PluginModuleChromeParent::SetChildTimeout(const int32_t aChildTimeout) { int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) : MessageChannel::kNoTimeout; @@ -686,33 +658,24 @@ PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout) } void -PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule) +PluginModuleChromeParent::TimeoutChanged(const char* aPref, void* aModule) { - PluginModuleParent* module = static_cast(aModule); - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); #ifndef XP_WIN if (!strcmp(aPref, kChildTimeoutPref)) { - MOZ_ASSERT(module->IsChrome()); // The timeout value used by the parent for children int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0); - module->SetChildTimeout(timeoutSecs); + static_cast(aModule)->SetChildTimeout(timeoutSecs); #else if (!strcmp(aPref, kChildTimeoutPref) || !strcmp(aPref, kHangUIMinDisplayPref) || !strcmp(aPref, kHangUITimeoutPref)) { - MOZ_ASSERT(module->IsChrome()); - static_cast(module)->EvaluateHangUIState(true); + static_cast(aModule)->EvaluateHangUIState(true); #endif // XP_WIN } else if (!strcmp(aPref, kParentTimeoutPref)) { // The timeout value used by the child for its parent - MOZ_ASSERT(module->IsChrome()); int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0); - unused << static_cast(module)->SendSetParentHangTimeout(timeoutSecs); - } else if (!strcmp(aPref, kContentTimeoutPref)) { - MOZ_ASSERT(!module->IsChrome()); - int32_t timeoutSecs = Preferences::GetInt(kContentTimeoutPref, 0); - module->SetChildTimeout(timeoutSecs); + unused << static_cast(aModule)->SendSetParentHangTimeout(timeoutSecs); } } @@ -896,23 +859,6 @@ PluginModuleChromeParent::ShouldContinueFromReplyTimeout() return false; } -bool -PluginModuleContentParent::ShouldContinueFromReplyTimeout() -{ - nsRefPtr monitor = ProcessHangMonitor::Get(); - if (!monitor) { - return true; - } - monitor->NotifyPluginHang(mPluginId); - return true; -} - -void -PluginModuleContentParent::OnExitedSyncSend() -{ - ProcessHangMonitor::ClearHang(); -} - void PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop) { diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h index 88c79cf31ef9..f100f8cd77dd 100644 --- a/dom/plugins/ipc/PluginModuleParent.h +++ b/dom/plugins/ipc/PluginModuleParent.h @@ -176,9 +176,6 @@ protected: PluginAsyncSurrogate** aSurrogate = nullptr); protected: - void SetChildTimeout(const int32_t aChildTimeout); - static void TimeoutChanged(const char* aPref, void* aModule); - virtual void UpdatePluginTimeout() {} virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE { return true; } @@ -312,19 +309,13 @@ class PluginModuleContentParent : public PluginModuleParent static void OnLoadPluginResult(const uint32_t& aPluginId, const bool& aResult); static void AssociatePluginId(uint32_t aPluginId, base::ProcessId aProcessId); - virtual ~PluginModuleContentParent(); - private: - virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE; - virtual void OnExitedSyncSend() MOZ_OVERRIDE; #ifdef MOZ_CRASHREPORTER_INJECTOR void OnCrash(DWORD processID) MOZ_OVERRIDE {} #endif static PluginModuleContentParent* sSavedModuleParent; - - uint32_t mPluginId; }; class PluginModuleChromeParent @@ -352,9 +343,6 @@ class PluginModuleChromeParent */ void OnHangUIContinue(); - - void - EvaluateHangUIState(const bool aReset); #endif // XP_WIN virtual bool WaitForIPCConnection() MOZ_OVERRIDE; @@ -414,6 +402,8 @@ private: CrashReporterParent* CrashReporter(); void CleanupFromTimeout(const bool aByHangUI); + void SetChildTimeout(const int32_t aChildTimeout); + static void TimeoutChanged(const char* aPref, void* aModule); virtual void UpdatePluginTimeout() MOZ_OVERRIDE; @@ -459,6 +449,9 @@ private: #endif // MOZ_CRASHREPORTER + void + EvaluateHangUIState(const bool aReset); + /** * Launches the Plugin Hang UI. * diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index bd5c01c14219..57ed038b3789 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -177,11 +177,6 @@ public: return INTR_SEMS == mMesageSemantics && OUT_MESSAGE == mDirection; } - bool IsOutgoingSync() const { - return (mMesageSemantics == INTR_SEMS || mMesageSemantics == SYNC_SEMS) && - mDirection == OUT_MESSAGE; - } - void Describe(int32_t* id, const char** dir, const char** sems, const char** name) const { @@ -223,9 +218,6 @@ public: if (frame.IsInterruptIncall()) mThat.EnteredCall(); - if (frame.IsOutgoingSync()) - mThat.EnteredSyncSend(); - mThat.mSawInterruptOutMsg |= frame.IsInterruptOutcall(); } @@ -234,9 +226,7 @@ public: MOZ_ASSERT(!mThat.mCxxStackFrames.empty()); - const InterruptFrame& frame = mThat.mCxxStackFrames.back(); - bool exitingSync = frame.IsOutgoingSync(); - bool exitingCall = frame.IsInterruptIncall(); + bool exitingCall = mThat.mCxxStackFrames.back().IsInterruptIncall(); mThat.mCxxStackFrames.shrinkBy(1); bool exitingStack = mThat.mCxxStackFrames.empty(); @@ -249,9 +239,6 @@ public: if (exitingCall) mThat.ExitedCall(); - if (exitingSync) - mThat.ExitedSyncSend(); - if (exitingStack) mThat.ExitedCxxStack(); } diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index a0e2ebbc8758..894f075696a6 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -291,14 +291,6 @@ class MessageChannel : HasResultCodes mListener->OnExitedCall(); } - void EnteredSyncSend() { - mListener->OnEnteredSyncSend(); - } - - void ExitedSyncSend() { - mListener->OnExitedSyncSend(); - } - MessageListener *Listener() const { return mListener.get(); } diff --git a/ipc/glue/MessageLink.h b/ipc/glue/MessageLink.h index 7004019e1c4f..069024c7799e 100644 --- a/ipc/glue/MessageLink.h +++ b/ipc/glue/MessageLink.h @@ -98,11 +98,6 @@ class MessageListener return RIPChildWins; } - virtual void OnEnteredSyncSend() { - } - virtual void OnExitedSyncSend() { - } - virtual void ProcessRemoteNativeEventsInInterruptCall() { } diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 174ddb9abaff..cea5e1c84acf 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -40,7 +40,6 @@ #include "mozilla/dom/WindowBinding.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" -#include "mozilla/ProcessHangMonitor.h" #include "AccessCheck.h" #include "nsGlobalWindow.h" #include "nsAboutProtocolUtils.h" @@ -1113,7 +1112,6 @@ class Watchdog #include "ipc/Nuwa.h" #endif -#define PREF_MAX_SCRIPT_RUN_TIME_CHILD "dom.max_child_script_run_time" #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time" #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time" @@ -1136,7 +1134,6 @@ class WatchdogManager : public nsIObserver mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog"); mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT); mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME); - mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHILD); } protected: @@ -1150,7 +1147,6 @@ class WatchdogManager : public nsIObserver mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog"); mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT); mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME); - mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHILD); } public: @@ -1228,10 +1224,7 @@ class WatchdogManager : public nsIObserver int32_t chromeTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CHROME, 20); if (chromeTime <= 0) chromeTime = INT32_MAX; - int32_t childTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CHILD, 3); - if (childTime <= 0) - childTime = INT32_MAX; - mWatchdog->SetMinScriptRunTimeSeconds(std::min(std::min(contentTime, chromeTime), childTime)); + mWatchdog->SetMinScriptRunTimeSeconds(std::min(contentTime, chromeTime)); } } @@ -1348,10 +1341,6 @@ XPCJSRuntime::DefaultJSContextCallback(JSRuntime *rt) void XPCJSRuntime::ActivityCallback(void *arg, bool active) { - if (!active) { - ProcessHangMonitor::ClearHang(); - } - XPCJSRuntime* self = static_cast(arg); self->mWatchdogManager->RecordRuntimeActivity(active); } @@ -1390,16 +1379,13 @@ XPCJSRuntime::InterruptCallback(JSContext *cx) if (!nsContentUtils::IsInitialized()) return true; - bool contentProcess = XRE_GetProcessType() == GeckoProcessType_Content; - // This is at least the second interrupt callback we've received since // returning to the event loop. See how long it's been, and what the limit // is. TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint; bool chrome = nsContentUtils::IsCallerChrome(); - const char *prefName = contentProcess ? PREF_MAX_SCRIPT_RUN_TIME_CHILD - : chrome ? PREF_MAX_SCRIPT_RUN_TIME_CHROME - : PREF_MAX_SCRIPT_RUN_TIME_CONTENT; + const char *prefName = chrome ? PREF_MAX_SCRIPT_RUN_TIME_CHROME + : PREF_MAX_SCRIPT_RUN_TIME_CONTENT; int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10); // If there's no limit, or we're within the limit, let it go. @@ -1437,9 +1423,7 @@ XPCJSRuntime::InterruptCallback(JSContext *cx) // The user chose to continue the script. Reset the timer, and disable this // machinery with a pref of the user opted out of future slow-script dialogs. - if (response != nsGlobalWindow::ContinueSlowScriptAndKeepNotifying) - self->mSlowScriptCheckpoint = TimeStamp::NowLoRes(); - + self->mSlowScriptCheckpoint = TimeStamp::NowLoRes(); if (response == nsGlobalWindow::AlwaysContinueSlowScript) Preferences::SetInt(prefName, 0); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 6c77a246ce21..0c1cf4a83e7e 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2278,7 +2278,6 @@ pref("editor.positioning.offset", 0); pref("dom.use_watchdog", true); pref("dom.max_chrome_script_run_time", 20); -pref("dom.max_child_script_run_time", 2); pref("dom.max_script_run_time", 10); // If true, ArchiveReader will be enabled @@ -2325,9 +2324,6 @@ pref("dom.ipc.plugins.timeoutSecs", 45); // to a synchronous request before terminating itself. After this // point the child assumes the parent is hung. Currently disabled. pref("dom.ipc.plugins.parentTimeoutSecs", 0); -// How long a plugin in e10s is allowed to process a synchronous IPC -// message before we notify the chrome process of a hang. -pref("dom.ipc.plugins.contentTimeoutSecs", 2); // How long a plugin launch is allowed to take before // we consider it failed. pref("dom.ipc.plugins.processLaunchTimeoutSecs", 45); @@ -2345,7 +2341,6 @@ pref("dom.ipc.tabs.shutdownTimeoutSecs", 5); #else // No timeout in DEBUG builds pref("dom.ipc.plugins.timeoutSecs", 0); -pref("dom.ipc.plugins.contentTimeoutSecs", 0); pref("dom.ipc.plugins.processLaunchTimeoutSecs", 0); pref("dom.ipc.plugins.parentTimeoutSecs", 0); #ifdef XP_WIN diff --git a/testing/marionette/client/marionette/geckoinstance.py b/testing/marionette/client/marionette/geckoinstance.py index 1f0d9c51a0ac..0bc1a1477152 100644 --- a/testing/marionette/client/marionette/geckoinstance.py +++ b/testing/marionette/client/marionette/geckoinstance.py @@ -28,8 +28,7 @@ class GeckoInstance(object): "browser.displayedE10SPrompt.3": 5, "browser.displayedE10SPrompt.4": 5, "browser.tabs.remote.autostart.1": False, - "browser.tabs.remote.autostart.2": False, - "dom.ipc.reportProcessHangs": False} + "browser.tabs.remote.autostart.2": False} def __init__(self, host, port, bin, profile=None, app_args=None, symbols_path=None, gecko_log=None, prefs=None): diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index 7769d1ba91b6..e18fe96c053d 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -14,8 +14,6 @@ user_pref("dom.forms.color", true); // on for testing user_pref("dom.max_script_run_time", 0); // no slow script dialogs user_pref("hangmonitor.timeout", 0); // no hang monitor user_pref("dom.max_chrome_script_run_time", 0); -user_pref("dom.max_child_script_run_time", 0); -user_pref("dom.ipc.reportProcessHangs", false); // process hang monitor user_pref("dom.popup_maximum", -1); user_pref("dom.send_after_paint_to_content", true); user_pref("dom.successive_dialog_time_limit", 0); From 5f582f7ec0c8e6cd81a346a49e4e9533f6fff225 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 16 Jan 2015 15:21:25 -0800 Subject: [PATCH 131/133] Backed out 2 changesets (bug 1121297) for Cpp bustage on a CLOSED TREE Backed out changeset 28960bb167ef (bug 1121297) Backed out changeset 005cf05954e7 (bug 1121297) --HG-- rename : memory/volatile/VolatileBuffer.h => memory/mozalloc/VolatileBuffer.h rename : memory/volatile/VolatileBufferAshmem.cpp => memory/mozalloc/VolatileBufferAshmem.cpp rename : memory/volatile/VolatileBufferFallback.cpp => memory/mozalloc/VolatileBufferFallback.cpp rename : memory/volatile/VolatileBufferOSX.cpp => memory/mozalloc/VolatileBufferOSX.cpp rename : memory/volatile/VolatileBufferWindows.cpp => memory/mozalloc/VolatileBufferWindows.cpp rename : memory/volatile/tests/TestVolatileBuffer.cpp => memory/mozalloc/tests/TestVolatileBuffer.cpp rename : memory/volatile/tests/moz.build => memory/mozalloc/tests/moz.build --- .../{volatile => mozalloc}/VolatileBuffer.h | 18 +-- .../VolatileBufferAshmem.cpp | 9 +- .../VolatileBufferFallback.cpp | 17 +-- .../VolatileBufferOSX.cpp | 9 +- .../VolatileBufferWindows.cpp | 13 +- memory/mozalloc/moz.build | 20 +++ memory/mozalloc/tests/TestVolatileBuffer.cpp | 143 ++++++++++++++++++ memory/{volatile => mozalloc}/tests/moz.build | 10 +- memory/volatile/moz.build | 33 ---- memory/volatile/tests/TestVolatileBuffer.cpp | 102 ------------- moz.build | 1 - 11 files changed, 180 insertions(+), 195 deletions(-) rename memory/{volatile => mozalloc}/VolatileBuffer.h (91%) rename memory/{volatile => mozalloc}/VolatileBufferAshmem.cpp (93%) rename memory/{volatile => mozalloc}/VolatileBufferFallback.cpp (80%) rename memory/{volatile => mozalloc}/VolatileBufferOSX.cpp (93%) rename memory/{volatile => mozalloc}/VolatileBufferWindows.cpp (94%) create mode 100644 memory/mozalloc/tests/TestVolatileBuffer.cpp rename memory/{volatile => mozalloc}/tests/moz.build (74%) delete mode 100644 memory/volatile/moz.build delete mode 100644 memory/volatile/tests/TestVolatileBuffer.cpp diff --git a/memory/volatile/VolatileBuffer.h b/memory/mozalloc/VolatileBuffer.h similarity index 91% rename from memory/volatile/VolatileBuffer.h rename to memory/mozalloc/VolatileBuffer.h index e82f755a824b..54bd88c743ae 100644 --- a/memory/volatile/VolatileBuffer.h +++ b/memory/mozalloc/VolatileBuffer.h @@ -6,7 +6,6 @@ #define mozalloc_VolatileBuffer_h #include "mozilla/mozalloc.h" -#include "mozilla/Mutex.h" #include "mozilla/RefPtr.h" #include "mozilla/MemoryReporting.h" @@ -37,19 +36,19 @@ * When a buffer is purged, some or all of the buffer is zeroed out. This * API cannot tell which parts of the buffer were lost. * - * VolatileBuffer and VolatileBufferPtr are threadsafe. + * VolatileBuffer is not thread safe. Do not use VolatileBufferPtrs on + * different threads. */ namespace mozilla { -class VolatileBuffer +class MOZALLOC_EXPORT VolatileBuffer : public RefCounted { friend class VolatileBufferPtr_base; public: MOZ_DECLARE_REFCOUNTED_TYPENAME(VolatileBuffer) - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VolatileBuffer) - VolatileBuffer(); + ~VolatileBuffer(); /* aAlignment must be a multiple of the pointer size */ bool Init(size_t aSize, size_t aAlignment = sizeof(void*)); @@ -63,15 +62,6 @@ protected: void Unlock(); private: - ~VolatileBuffer(); - - /** - * Protects mLockCount, mFirstLock, and changes to the volatility of our - * buffer. Other member variables are read-only except in Init() and the - * destructor. - */ - Mutex mMutex; - void* mBuf; size_t mSize; int mLockCount; diff --git a/memory/volatile/VolatileBufferAshmem.cpp b/memory/mozalloc/VolatileBufferAshmem.cpp similarity index 93% rename from memory/volatile/VolatileBufferAshmem.cpp rename to memory/mozalloc/VolatileBufferAshmem.cpp index 09904d6efe01..ea34df263862 100644 --- a/memory/volatile/VolatileBufferAshmem.cpp +++ b/memory/mozalloc/VolatileBufferAshmem.cpp @@ -22,8 +22,7 @@ extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size); namespace mozilla { VolatileBuffer::VolatileBuffer() - : mMutex("VolatileBuffer") - , mBuf(nullptr) + : mBuf(nullptr) , mSize(0) , mLockCount(0) , mFd(-1) @@ -73,8 +72,6 @@ heap_alloc: VolatileBuffer::~VolatileBuffer() { - MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?"); - if (OnHeap()) { free(mBuf); } else { @@ -86,8 +83,6 @@ VolatileBuffer::~VolatileBuffer() bool VolatileBuffer::Lock(void** aBuf) { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer"); *aBuf = mBuf; @@ -103,8 +98,6 @@ VolatileBuffer::Lock(void** aBuf) void VolatileBuffer::Unlock() { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!"); if (--mLockCount || OnHeap()) { return; diff --git a/memory/volatile/VolatileBufferFallback.cpp b/memory/mozalloc/VolatileBufferFallback.cpp similarity index 80% rename from memory/volatile/VolatileBufferFallback.cpp rename to memory/mozalloc/VolatileBufferFallback.cpp index f4bfe39c6487..9445b6067399 100644 --- a/memory/volatile/VolatileBufferFallback.cpp +++ b/memory/mozalloc/VolatileBufferFallback.cpp @@ -13,8 +13,7 @@ int posix_memalign(void** memptr, size_t alignment, size_t size); namespace mozilla { VolatileBuffer::VolatileBuffer() - : mMutex("VolatileBuffer") - , mBuf(nullptr) + : mBuf(nullptr) , mSize(0) , mLockCount(0) { @@ -28,13 +27,9 @@ bool VolatileBuffer::Init(size_t aSize, size_t aAlignment) mSize = aSize; #if defined(MOZ_MEMORY) - if (posix_memalign(&mBuf, aAlignment, aSize) != 0) { - return false; - } + posix_memalign(&mBuf, aAlignment, aSize); #elif defined(HAVE_POSIX_MEMALIGN) - if (moz_posix_memalign(&mBuf, aAlignment, aSize) != 0) { - return false; - } + (void)moz_posix_memalign(&mBuf, aAlignment, aSize); #else #error "No memalign implementation found" #endif @@ -43,16 +38,12 @@ bool VolatileBuffer::Init(size_t aSize, size_t aAlignment) VolatileBuffer::~VolatileBuffer() { - MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?"); - free(mBuf); } bool VolatileBuffer::Lock(void** aBuf) { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer"); *aBuf = mBuf; @@ -64,8 +55,6 @@ VolatileBuffer::Lock(void** aBuf) void VolatileBuffer::Unlock() { - MutexAutoLock lock(mMutex); - mLockCount--; MOZ_ASSERT(mLockCount >= 0, "VolatileBuffer unlocked too many times!"); } diff --git a/memory/volatile/VolatileBufferOSX.cpp b/memory/mozalloc/VolatileBufferOSX.cpp similarity index 93% rename from memory/volatile/VolatileBufferOSX.cpp rename to memory/mozalloc/VolatileBufferOSX.cpp index af39bcae166b..3f567d4f35ba 100644 --- a/memory/volatile/VolatileBufferOSX.cpp +++ b/memory/mozalloc/VolatileBufferOSX.cpp @@ -16,8 +16,7 @@ namespace mozilla { VolatileBuffer::VolatileBuffer() - : mMutex("VolatileBuffer") - , mBuf(nullptr) + : mBuf(nullptr) , mSize(0) , mLockCount(0) , mHeap(false) @@ -54,8 +53,6 @@ heap_alloc: VolatileBuffer::~VolatileBuffer() { - MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?"); - if (OnHeap()) { free(mBuf); } else { @@ -66,8 +63,6 @@ VolatileBuffer::~VolatileBuffer() bool VolatileBuffer::Lock(void** aBuf) { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer"); *aBuf = mBuf; @@ -87,8 +82,6 @@ VolatileBuffer::Lock(void** aBuf) void VolatileBuffer::Unlock() { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!"); if (--mLockCount || OnHeap()) { return; diff --git a/memory/volatile/VolatileBufferWindows.cpp b/memory/mozalloc/VolatileBufferWindows.cpp similarity index 94% rename from memory/volatile/VolatileBufferWindows.cpp rename to memory/mozalloc/VolatileBufferWindows.cpp index b12e0eccb533..2aba1e2747ff 100644 --- a/memory/volatile/VolatileBufferWindows.cpp +++ b/memory/mozalloc/VolatileBufferWindows.cpp @@ -2,6 +2,10 @@ * 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/. */ +#if defined(XP_WIN) +# define MOZALLOC_EXPORT __declspec(dllexport) +#endif + #include "VolatileBuffer.h" #include "mozilla/Assertions.h" #include "mozilla/mozalloc.h" @@ -22,8 +26,7 @@ extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size); namespace mozilla { VolatileBuffer::VolatileBuffer() - : mMutex("VolatileBuffer") - , mBuf(nullptr) + : mBuf(nullptr) , mSize(0) , mLockCount(0) , mHeap(false) @@ -69,8 +72,6 @@ heap_alloc: VolatileBuffer::~VolatileBuffer() { - MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?"); - if (OnHeap()) { #ifdef MOZ_MEMORY free(mBuf); @@ -85,8 +86,6 @@ VolatileBuffer::~VolatileBuffer() bool VolatileBuffer::Lock(void** aBuf) { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer"); *aBuf = mBuf; @@ -112,8 +111,6 @@ VolatileBuffer::Lock(void** aBuf) void VolatileBuffer::Unlock() { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!"); if (--mLockCount || OnHeap()) { return; diff --git a/memory/mozalloc/moz.build b/memory/mozalloc/moz.build index 72640496c416..aae26cb08f6c 100644 --- a/memory/mozalloc/moz.build +++ b/memory/mozalloc/moz.build @@ -10,6 +10,7 @@ EXPORTS.mozilla += [ 'mozalloc.h', 'mozalloc_abort.h', 'mozalloc_oom.h', + 'VolatileBuffer.h', ] if CONFIG['MOZ_MSVC_STL_WRAP__RAISE'] or CONFIG['MOZ_MSVC_STL_WRAP__Throw']: @@ -39,6 +40,23 @@ UNIFIED_SOURCES += [ 'mozalloc_oom.cpp', ] +if CONFIG['OS_TARGET'] == 'Android': + UNIFIED_SOURCES += [ + 'VolatileBufferAshmem.cpp', + ] +elif CONFIG['OS_TARGET'] == 'Darwin': + UNIFIED_SOURCES += [ + 'VolatileBufferOSX.cpp', + ] +elif CONFIG['OS_TARGET'] == 'WINNT': + UNIFIED_SOURCES += [ + 'VolatileBufferWindows.cpp', + ] +else: + UNIFIED_SOURCES += [ + 'VolatileBufferFallback.cpp', + ] + if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': Library('mozalloc') else: @@ -48,6 +66,8 @@ else: # The strndup declaration in string.h is in an ifdef __USE_GNU section DEFINES['_GNU_SOURCE'] = True +TEST_DIRS += ['tests'] + GENERATED_INCLUDES += ['/xpcom'] DISABLE_STL_WRAPPING = True diff --git a/memory/mozalloc/tests/TestVolatileBuffer.cpp b/memory/mozalloc/tests/TestVolatileBuffer.cpp new file mode 100644 index 000000000000..6bd3194295d0 --- /dev/null +++ b/memory/mozalloc/tests/TestVolatileBuffer.cpp @@ -0,0 +1,143 @@ +/* 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 "TestHarness.h" +#include "mozilla/VolatileBuffer.h" +#include + +#if defined(ANDROID) +#include +#include +#include +#include +#include +#elif defined(XP_DARWIN) +#include +#endif + +using namespace mozilla; + +int main(int argc, char **argv) +{ + ScopedXPCOM xpcom("VolatileBufferTests"); + if (xpcom.failed()) { + return 1; + } + + RefPtr heapbuf = new VolatileBuffer(); + if (!heapbuf || !heapbuf->Init(512)) { + fail("Failed to initialize VolatileBuffer"); + return 1; + } + + { + VolatileBufferPtr ptr(heapbuf); + if (ptr.WasBufferPurged()) { + fail("Buffer was immediately purged after initialization"); + return 1; + } + + if (!ptr) { + fail("Didn't get a pointer"); + return 1; + } + } + + RefPtr buf = new VolatileBuffer(); + if (!buf || !buf->Init(16384)) { + fail("Failed to initialize VolatileBuffer"); + return 1; + } + + const char teststr[] = "foobar"; + + { + VolatileBufferPtr ptr(buf); + if (ptr.WasBufferPurged()) { + fail("Buffer should not be purged immediately after initialization"); + return 1; + } + + if (!ptr) { + fail("Didn't get a pointer"); + return 1; + } + + { + VolatileBufferPtr ptr2(buf); + if (ptr2.WasBufferPurged()) { + fail("Failed to Lock buffer again while currently locked"); + return 1; + } + + if (!ptr2) { + fail("Didn't get a pointer on the second lock"); + return 1; + } + + strcpy(ptr2, teststr); + } + } + + { + VolatileBufferPtr ptr(buf); + if (ptr.WasBufferPurged()) { + fail("Buffer was immediately purged after unlock"); + return 1; + } + + if (strcmp(ptr, teststr)) { + fail("Buffer failed to retain data after unlock"); + return 1; + } + } + + // Test purging if we know how to +#if defined(MOZ_WIDGET_GONK) + // This also works on Android, but we need root. + int fd = open("/" ASHMEM_NAME_DEF, O_RDWR); + if (fd < 0) { + fail("Failed to open ashmem device"); + return 1; + } + if (ioctl(fd, ASHMEM_PURGE_ALL_CACHES, NULL) < 0) { + fail("Failed to purge ashmem caches"); + return 1; + } +#elif defined(XP_DARWIN) + int state; + vm_purgable_control(mach_task_self(), (vm_address_t)NULL, + VM_PURGABLE_PURGE_ALL, &state); +#else + return 0; +#endif + + if (!buf->NonHeapSizeOfExcludingThis()) { + fail("Buffer should not be allocated on heap"); + return 1; + } + + { + VolatileBufferPtr ptr(buf); + if (!ptr.WasBufferPurged()) { + fail("Buffer should not be unpurged after forced purge"); + return 1; + } + + if (!strcmp(ptr, teststr)) { + fail("Purge did not actually purge data"); + return 1; + } + } + + { + VolatileBufferPtr ptr(buf); + if (ptr.WasBufferPurged()) { + fail("Buffer still purged after lock"); + return 1; + } + } + + return 0; +} diff --git a/memory/volatile/tests/moz.build b/memory/mozalloc/tests/moz.build similarity index 74% rename from memory/volatile/tests/moz.build rename to memory/mozalloc/tests/moz.build index e792d6887e52..3627343462c5 100644 --- a/memory/volatile/tests/moz.build +++ b/memory/mozalloc/tests/moz.build @@ -4,10 +4,6 @@ # 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/. -UNIFIED_SOURCES = [ - 'TestVolatileBuffer.cpp', -] - -FINAL_LIBRARY = 'xul-gtest' - -FAIL_ON_WARNINGS = True +GeckoCppUnitTests([ + 'TestVolatileBuffer', +]) diff --git a/memory/volatile/moz.build b/memory/volatile/moz.build deleted file mode 100644 index 044c9f4dd88a..000000000000 --- a/memory/volatile/moz.build +++ /dev/null @@ -1,33 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. -NO_VISIBILITY_FLAGS = True - -EXPORTS.mozilla += [ - 'VolatileBuffer.h', -] - -if CONFIG['OS_TARGET'] == 'Android': - UNIFIED_SOURCES += [ - 'VolatileBufferAshmem.cpp', - ] -elif CONFIG['OS_TARGET'] == 'Darwin': - UNIFIED_SOURCES += [ - 'VolatileBufferOSX.cpp', - ] -elif CONFIG['OS_TARGET'] == 'WINNT': - UNIFIED_SOURCES += [ - 'VolatileBufferWindows.cpp', - ] -else: - UNIFIED_SOURCES += [ - 'VolatileBufferFallback.cpp', - ] - -FINAL_LIBRARY = 'xul' - -TEST_DIRS += ['tests'] - -FAIL_ON_WARNINGS = True diff --git a/memory/volatile/tests/TestVolatileBuffer.cpp b/memory/volatile/tests/TestVolatileBuffer.cpp deleted file mode 100644 index 9a4a8781d7f8..000000000000 --- a/memory/volatile/tests/TestVolatileBuffer.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* 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 "gtest/gtest.h" -#include "mozilla/VolatileBuffer.h" -#include - -#if defined(ANDROID) -#include -#include -#include -#include -#include -#elif defined(XP_DARWIN) -#include -#endif - -using namespace mozilla; - -TEST(VolatileBufferTest, HeapVolatileBuffersWork) -{ - RefPtr heapbuf = new VolatileBuffer(); - - ASSERT_TRUE(heapbuf) << "Failed to create VolatileBuffer"; - ASSERT_TRUE(heapbuf->Init(512)) << "Failed to initialize VolatileBuffer"; - - VolatileBufferPtr ptr(heapbuf); - - EXPECT_FALSE(ptr.WasBufferPurged()) - << "Buffer should not be purged immediately after initialization"; - EXPECT_TRUE(ptr) << "Couldn't get pointer from VolatileBufferPtr"; -} - -TEST(VolatileBufferTest, RealVolatileBuffersWork) -{ - RefPtr buf = new VolatileBuffer(); - - ASSERT_TRUE(buf) << "Failed to create VolatileBuffer"; - ASSERT_TRUE(buf->Init(16384)) << "Failed to initialize VolatileBuffer"; - - const char teststr[] = "foobar"; - - { - VolatileBufferPtr ptr(buf); - - EXPECT_FALSE(ptr.WasBufferPurged()) - << "Buffer should not be purged immediately after initialization"; - EXPECT_TRUE(ptr) << "Couldn't get pointer from VolatileBufferPtr"; - - { - VolatileBufferPtr ptr2(buf); - - EXPECT_FALSE(ptr.WasBufferPurged()) - << "Failed to lock buffer again while currently locked"; - ASSERT_TRUE(ptr2) << "Didn't get a pointer on the second lock"; - - strcpy(ptr2, teststr); - } - } - - { - VolatileBufferPtr ptr(buf); - - EXPECT_FALSE(ptr.WasBufferPurged()) - << "Buffer was immediately purged after unlock"; - EXPECT_STREQ(ptr, teststr) << "Buffer failed to retain data after unlock"; - } - - // Test purging if we know how to -#if defined(MOZ_WIDGET_GONK) - // This also works on Android, but we need root. - int fd = open("/" ASHMEM_NAME_DEF, O_RDWR); - - ASSERT_GE(fd, 0) << "Failed to open ashmem device"; - ASSERT_GE(ioctl(fd, ASHMEM_PURGE_ALL_CACHES, NULL), 0) - << "Failed to purge ashmem caches"; -#elif defined(XP_DARWIN) - int state; - vm_purgable_control(mach_task_self(), (vm_address_t)NULL, - VM_PURGABLE_PURGE_ALL, &state); -#else - return; -#endif - - EXPECT_GT(buf->NonHeapSizeOfExcludingThis(), 0ul) - << "Buffer should not be allocated on heap"; - - { - VolatileBufferPtr ptr(buf); - - EXPECT_TRUE(ptr.WasBufferPurged()) - << "Buffer should not be unpurged after forced purge"; - EXPECT_STRNE(ptr, teststr) << "Purge did not actually purge data"; - } - - { - VolatileBufferPtr ptr(buf); - - EXPECT_FALSE(ptr.WasBufferPurged()) << "Buffer still purged after lock"; - } -} diff --git a/moz.build b/moz.build index 1c97f6f9b5c0..d5d7cf32b305 100644 --- a/moz.build +++ b/moz.build @@ -44,7 +44,6 @@ if not CONFIG['LIBXUL_SDK']: DIRS += [ 'mozglue', 'memory/mozalloc', - 'memory/volatile', ] if not CONFIG['JS_STANDALONE']: From ec24548ebe8cc44469e43bbaff8c5dc89bc2547b Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 16 Jan 2015 15:23:03 -0800 Subject: [PATCH 132/133] Touch CLOBBER for bug 1118618's backout to fix build bustage on a CLOSED TREE --- CLOBBER | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLOBBER b/CLOBBER index 1b0ac3bd9e3f..5c929901aaee 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 1101553 - remove nsPIPlacesHistoryListenersNotifier +Bug 1118618's backout needed a clobber From 3f1298938cde09b73c669e4543a77974ac6825a3 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 16 Jan 2015 16:34:06 -0800 Subject: [PATCH 133/133] Bug 850197 - Mark layout/reftests/svg/as-image/list-simple-1.html random on Windows and OS X on a CLOSED TREE. a=KWierso --HG-- extra : amend_source : dc810842bccef821869ed676b6c216f0bb78e4f0 --- layout/reftests/svg/as-image/reftest.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/reftests/svg/as-image/reftest.list b/layout/reftests/svg/as-image/reftest.list index 8809159753f2..c2534abb6687 100644 --- a/layout/reftests/svg/as-image/reftest.list +++ b/layout/reftests/svg/as-image/reftest.list @@ -127,7 +127,7 @@ skip-if(B2G) == img-fragment-2a.html img-fragment-2-ref.html # bug 773482 skip-if(B2G) == img-fragment-2b.html img-fragment-2-ref.html # bug 773482 skip-if(B2G) == img-fragment-2c.html img-fragment-2-ref.html # bug 773482 -fuzzy-if(B2G,68,4) == list-simple-1.html list-simple-1-ref.html +fuzzy-if(B2G,68,4) random-if(winWidget||cocoaWidget) == list-simple-1.html list-simple-1-ref.html == svg-image-simple-1.svg lime100x100.svg == svg-image-simple-2.svg lime100x100.svg