| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/disk_cache/rankings.h" | 5 #include "net/disk_cache/rankings.h" |
| 6 | 6 |
| 7 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
| 8 #include "net/disk_cache/backend_impl.h" | 8 #include "net/disk_cache/backend_impl.h" |
| 9 #include "net/disk_cache/entry_impl.h" | 9 #include "net/disk_cache/entry_impl.h" |
| 10 #include "net/disk_cache/errors.h" | 10 #include "net/disk_cache/errors.h" |
| (...skipping 706 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 717 next.Data()->prev = prev.address().value(); | 717 next.Data()->prev = prev.address().value(); |
| 718 WriteTail(my_list); | 718 WriteTail(my_list); |
| 719 } | 719 } |
| 720 | 720 |
| 721 next.Store(); | 721 next.Store(); |
| 722 prev.Store(); | 722 prev.Store(); |
| 723 control_data_->transaction = 0; | 723 control_data_->transaction = 0; |
| 724 control_data_->operation = 0; | 724 control_data_->operation = 0; |
| 725 } | 725 } |
| 726 | 726 |
| 727 bool Rankings::CheckEntry(CacheRankingsBlock* rankings) { | |
| 728 if (rankings->VerifyHash()) | |
| 729 return true; | |
| 730 | |
| 731 // If this entry is not dirty, it is a serious problem. | |
| 732 return backend_->GetCurrentEntryId() != rankings->Data()->dirty; | |
| 733 } | |
| 734 | |
| 735 bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev, | 727 bool Rankings::CheckLinks(CacheRankingsBlock* node, CacheRankingsBlock* prev, |
| 736 CacheRankingsBlock* next, List* list) { | 728 CacheRankingsBlock* next, List* list) { |
| 737 CacheAddr node_addr = node->address().value(); | 729 CacheAddr node_addr = node->address().value(); |
| 738 if (prev->Data()->next == node_addr && | 730 if (prev->Data()->next == node_addr && |
| 739 next->Data()->prev == node_addr) { | 731 next->Data()->prev == node_addr) { |
| 740 // A regular linked node. | 732 // A regular linked node. |
| 741 return true; | 733 return true; |
| 742 } | 734 } |
| 743 | 735 |
| 744 Trace("CheckLinks 0x%x (0x%x 0x%x)", node_addr, | 736 Trace("CheckLinks 0x%x (0x%x 0x%x)", node_addr, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 780 LOG(ERROR) << "Inconsistent LRU."; | 772 LOG(ERROR) << "Inconsistent LRU."; |
| 781 | 773 |
| 782 backend_->CriticalError(ERR_INVALID_LINKS); | 774 backend_->CriticalError(ERR_INVALID_LINKS); |
| 783 return false; | 775 return false; |
| 784 } | 776 } |
| 785 | 777 |
| 786 return true; | 778 return true; |
| 787 } | 779 } |
| 788 | 780 |
| 789 int Rankings::CheckList(List list) { | 781 int Rankings::CheckList(List list) { |
| 790 Addr& my_head = heads_[list]; | 782 Addr last1, last2; |
| 791 Addr& my_tail = tails_[list]; | 783 int head_items; |
| 792 if (!my_head.is_initialized()) { | 784 int rv = CheckListSection(list, last1, last2, true, // Head to tail. |
| 793 if (!my_tail.is_initialized()) | 785 &last1, &last2, &head_items); |
| 794 return 0; | 786 if (rv == 0) |
| 795 // If there is no head, having a tail is an error. | 787 return head_items; |
| 796 return ERR_INVALID_TAIL; | 788 |
| 789 Addr last3, last4; |
| 790 int tail_items; |
| 791 int rv2 = CheckListSection(list, last1, last2, false, // Tail to head. |
| 792 &last3, &last4, &tail_items); |
| 793 |
| 794 if (!head_items && rv != ERR_INVALID_NEXT) |
| 795 rv = ERR_INVALID_HEAD; |
| 796 |
| 797 if (!tail_items && rv2 != ERR_INVALID_NEXT) |
| 798 rv2 = ERR_INVALID_HEAD; |
| 799 |
| 800 int expected = control_data_->sizes[list]; |
| 801 int total_items = head_items + tail_items; |
| 802 |
| 803 if (!count_lists_) { |
| 804 // There's no expected value, so we'll use something else. If it looks like |
| 805 // we can rebuild the list, we lost at least one entry. |
| 806 if (last3 == last1 || last3 == last2 || last4 == last1 || last4 == last2) |
| 807 expected = total_items + 1; |
| 797 } | 808 } |
| 798 // If there is no tail, having a head is an error. | 809 |
| 799 if (!my_tail.is_initialized()) | 810 if (expected) { |
| 811 // This histogram has an offset so that we can see small negative values. In |
| 812 // practice, it is linear from -9 to +8. |
| 813 UMA_HISTOGRAM_CUSTOM_COUNTS("DiskCache.LostItems(Plus10)", |
| 814 expected - total_items + 10, 0, 2000, 75); |
| 815 } |
| 816 |
| 817 const int kInvalidHead = 1; |
| 818 const int kInvalidTail = 2; |
| 819 const int kInvalidHeadAndTail = 3; |
| 820 const int kOneInvalidEntry = 4; |
| 821 const int kTwoInvalidEntries = 5; |
| 822 const int kOneInvalidLink = 6; |
| 823 const int kTwoInvalidLinks = 7; |
| 824 const int kOneInvalidEntryOneInvalidLink = 8; |
| 825 |
| 826 int error = list * 10; |
| 827 if (rv == ERR_INVALID_HEAD && rv2 != ERR_INVALID_HEAD) { |
| 828 error += kInvalidHead; |
| 829 } else if (rv == ERR_INVALID_HEAD && rv2 == ERR_INVALID_HEAD) { |
| 830 error += kInvalidHeadAndTail; |
| 831 } else if (rv != ERR_INVALID_HEAD && rv2 == ERR_INVALID_HEAD) { |
| 832 error += kInvalidTail; |
| 833 } else if (rv == ERR_INVALID_ENTRY && rv2 == ERR_INVALID_ENTRY) { |
| 834 error += kTwoInvalidEntries; |
| 835 } else if (rv == ERR_INVALID_ENTRY && rv2 == 0) { |
| 836 error += kOneInvalidEntry; |
| 837 } else if (rv == ERR_INVALID_ENTRY || rv2 == ERR_INVALID_ENTRY) { |
| 838 error += kOneInvalidEntryOneInvalidLink; |
| 839 } else if (rv2 != 0) { |
| 840 error += kTwoInvalidLinks; |
| 841 } else { |
| 842 error += kOneInvalidLink; |
| 843 } |
| 844 CACHE_UMA(CACHE_ERROR, "ListErrorWithListId", 0, error); |
| 845 |
| 846 return rv; |
| 847 } |
| 848 |
| 849 // Note that the returned error codes assume a forward walk (from head to tail) |
| 850 // so they have to be adjusted accordingly by the caller. We use two stop values |
| 851 // to be able to detect a corrupt node at the end that is not linked going back. |
| 852 int Rankings::CheckListSection(List list, Addr end1, Addr end2, bool forward, |
| 853 Addr* last, Addr* second_last, int* num_items) { |
| 854 Addr current = forward ? heads_[list] : tails_[list]; |
| 855 *last = *second_last = current; |
| 856 *num_items = 0; |
| 857 if (!current.is_initialized()) |
| 858 return ERR_NO_ERROR; |
| 859 |
| 860 if (!current.SanityCheckForRankings()) |
| 800 return ERR_INVALID_HEAD; | 861 return ERR_INVALID_HEAD; |
| 801 | 862 |
| 802 if (my_tail.is_separate_file()) | |
| 803 return ERR_INVALID_TAIL; | |
| 804 | |
| 805 if (my_head.is_separate_file()) | |
| 806 return ERR_INVALID_HEAD; | |
| 807 | |
| 808 int num_items = 0; | |
| 809 Addr address(my_head.value()); | |
| 810 Addr prev(my_head.value()); | |
| 811 scoped_ptr<CacheRankingsBlock> node; | 863 scoped_ptr<CacheRankingsBlock> node; |
| 864 Addr prev_addr(current); |
| 812 do { | 865 do { |
| 813 node.reset(new CacheRankingsBlock(backend_->File(address), address)); | 866 node.reset(new CacheRankingsBlock(backend_->File(current), current)); |
| 814 node->Load(); | 867 node->Load(); |
| 815 if (node->Data()->prev != prev.value()) | 868 if (!SanityCheck(node.get(), true)) |
| 816 return ERR_INVALID_PREV; | |
| 817 if (!CheckEntry(node.get())) | |
| 818 return ERR_INVALID_ENTRY; | 869 return ERR_INVALID_ENTRY; |
| 819 | 870 |
| 820 prev.set_value(address.value()); | 871 CacheAddr next = forward ? node->Data()->next : node->Data()->prev; |
| 821 address.set_value(node->Data()->next); | 872 CacheAddr prev = forward ? node->Data()->prev : node->Data()->next; |
| 822 if (!address.is_initialized() || address.is_separate_file()) | 873 |
| 874 if (prev != prev_addr.value()) |
| 875 return ERR_INVALID_PREV; |
| 876 |
| 877 Addr next_addr(next); |
| 878 if (!next_addr.SanityCheckForRankings()) |
| 823 return ERR_INVALID_NEXT; | 879 return ERR_INVALID_NEXT; |
| 824 | 880 |
| 825 num_items++; | 881 prev_addr = current; |
| 826 } while (node->address().value() != address.value()); | 882 current = next_addr; |
| 827 return num_items; | 883 *second_last = *last; |
| 884 *last = current; |
| 885 (*num_items)++; |
| 886 |
| 887 if (next_addr == prev_addr) { |
| 888 Addr last = forward ? tails_[list] : heads_[list]; |
| 889 if (next_addr == last) |
| 890 return ERR_NO_ERROR; |
| 891 return ERR_INVALID_TAIL; |
| 892 } |
| 893 } while (current != end1 && current != end2); |
| 894 return ERR_NO_ERROR; |
| 828 } | 895 } |
| 829 | 896 |
| 830 bool Rankings::IsHead(CacheAddr addr, List* list) const { | 897 bool Rankings::IsHead(CacheAddr addr, List* list) const { |
| 831 for (int i = 0; i < LAST_ELEMENT; i++) { | 898 for (int i = 0; i < LAST_ELEMENT; i++) { |
| 832 if (addr == heads_[i].value()) { | 899 if (addr == heads_[i].value()) { |
| 833 if (*list != i) | 900 if (*list != i) |
| 834 Trace("Changing list %d to %d", *list, i); | 901 Trace("Changing list %d to %d", *list, i); |
| 835 *list = static_cast<List>(i); | 902 *list = static_cast<List>(i); |
| 836 return true; | 903 return true; |
| 837 } | 904 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 888 void Rankings::DecrementCounter(List list) { | 955 void Rankings::DecrementCounter(List list) { |
| 889 if (!count_lists_) | 956 if (!count_lists_) |
| 890 return; | 957 return; |
| 891 | 958 |
| 892 DCHECK(control_data_->sizes[list] > 0); | 959 DCHECK(control_data_->sizes[list] > 0); |
| 893 if (control_data_->sizes[list] > 0) | 960 if (control_data_->sizes[list] > 0) |
| 894 control_data_->sizes[list]--; | 961 control_data_->sizes[list]--; |
| 895 } | 962 } |
| 896 | 963 |
| 897 } // namespace disk_cache | 964 } // namespace disk_cache |
| OLD | NEW |