diff --git a/contrib/pg_visibility/Makefile b/contrib/pg_visibility/Makefile
index fbbaa2e..379591a 100644
--- a/contrib/pg_visibility/Makefile
+++ b/contrib/pg_visibility/Makefile
@@ -4,7 +4,7 @@ MODULE_big = pg_visibility
 OBJS = pg_visibility.o $(WIN32RES)
 
 EXTENSION = pg_visibility
-DATA = pg_visibility--1.0.sql
+DATA = pg_visibility--1.1.sql pg_visibility--1.0--1.1.sql
 PGFILEDESC = "pg_visibility - page visibility information"
 
 ifdef USE_PGXS
diff --git a/contrib/pg_visibility/pg_visibility--1.0--1.1.sql b/contrib/pg_visibility/pg_visibility--1.0--1.1.sql
new file mode 100644
index 0000000..494f42f
--- /dev/null
+++ b/contrib/pg_visibility/pg_visibility--1.0--1.1.sql
@@ -0,0 +1,15 @@
+/* contrib/pg_visibility/pg_visibility--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_visibility UPDATE TO '1.1'" to load this file. \quit
+
+
+CREATE FUNCTION pg_check_frozen(regclass, t_ctid OUT tid)
+RETURNS SETOF tid
+AS 'MODULE_PATHNAME', 'pg_check_frozen'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION pg_check_visible(regclass, t_ctid OUT tid)
+RETURNS SETOF tid
+AS 'MODULE_PATHNAME', 'pg_check_visible'
+LANGUAGE C STRICT;
diff --git a/contrib/pg_visibility/pg_visibility--1.0.sql b/contrib/pg_visibility/pg_visibility--1.0.sql
deleted file mode 100644
index da511e5..0000000
--- a/contrib/pg_visibility/pg_visibility--1.0.sql
+++ /dev/null
@@ -1,52 +0,0 @@
-/* contrib/pg_visibility/pg_visibility--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION pg_visibility" to load this file. \quit
-
--- Show visibility map information.
-CREATE FUNCTION pg_visibility_map(regclass, blkno bigint,
-								  all_visible OUT boolean,
-								  all_frozen OUT boolean)
-RETURNS record
-AS 'MODULE_PATHNAME', 'pg_visibility_map'
-LANGUAGE C STRICT;
-
--- Show visibility map and page-level visibility information.
-CREATE FUNCTION pg_visibility(regclass, blkno bigint,
-							  all_visible OUT boolean,
-							  all_frozen OUT boolean,
-							  pd_all_visible OUT boolean)
-RETURNS record
-AS 'MODULE_PATHNAME', 'pg_visibility'
-LANGUAGE C STRICT;
-
--- Show visibility map information for each block in a relation.
-CREATE FUNCTION pg_visibility_map(regclass, blkno OUT bigint,
-								  all_visible OUT boolean,
-								  all_frozen OUT boolean)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'pg_visibility_map_rel'
-LANGUAGE C STRICT;
-
--- Show visibility map and page-level visibility information for each block.
-CREATE FUNCTION pg_visibility(regclass, blkno OUT bigint,
-							  all_visible OUT boolean,
-							  all_frozen OUT boolean,
-							  pd_all_visible OUT boolean)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'pg_visibility_rel'
-LANGUAGE C STRICT;
-
--- Show summary of visibility map bits for a relation.
-CREATE FUNCTION pg_visibility_map_summary(regclass,
-    OUT all_visible bigint, OUT all_frozen bigint)
-RETURNS record
-AS 'MODULE_PATHNAME', 'pg_visibility_map_summary'
-LANGUAGE C STRICT;
-
--- Don't want these to be available to public.
-REVOKE ALL ON FUNCTION pg_visibility_map(regclass, bigint) FROM PUBLIC;
-REVOKE ALL ON FUNCTION pg_visibility(regclass, bigint) FROM PUBLIC;
-REVOKE ALL ON FUNCTION pg_visibility_map(regclass) FROM PUBLIC;
-REVOKE ALL ON FUNCTION pg_visibility(regclass) FROM PUBLIC;
-REVOKE ALL ON FUNCTION pg_visibility_map_summary(regclass) FROM PUBLIC;
diff --git a/contrib/pg_visibility/pg_visibility--1.1.sql b/contrib/pg_visibility/pg_visibility--1.1.sql
new file mode 100644
index 0000000..b49b644
--- /dev/null
+++ b/contrib/pg_visibility/pg_visibility--1.1.sql
@@ -0,0 +1,67 @@
+/* contrib/pg_visibility/pg_visibility--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pg_visibility" to load this file. \quit
+
+-- Show visibility map information.
+CREATE FUNCTION pg_visibility_map(regclass, blkno bigint,
+								  all_visible OUT boolean,
+								  all_frozen OUT boolean)
+RETURNS record
+AS 'MODULE_PATHNAME', 'pg_visibility_map'
+LANGUAGE C STRICT;
+
+-- Show visibility map and page-level visibility information.
+CREATE FUNCTION pg_visibility(regclass, blkno bigint,
+							  all_visible OUT boolean,
+							  all_frozen OUT boolean,
+							  pd_all_visible OUT boolean)
+RETURNS record
+AS 'MODULE_PATHNAME', 'pg_visibility'
+LANGUAGE C STRICT;
+
+-- Show visibility map information for each block in a relation.
+CREATE FUNCTION pg_visibility_map(regclass, blkno OUT bigint,
+								  all_visible OUT boolean,
+								  all_frozen OUT boolean)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_visibility_map_rel'
+LANGUAGE C STRICT;
+
+-- Show visibility map and page-level visibility information for each block.
+CREATE FUNCTION pg_visibility(regclass, blkno OUT bigint,
+							  all_visible OUT boolean,
+							  all_frozen OUT boolean,
+							  pd_all_visible OUT boolean)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_visibility_rel'
+LANGUAGE C STRICT;
+
+-- Show summary of visibility map bits for a relation.
+CREATE FUNCTION pg_visibility_map_summary(regclass,
+    OUT all_visible bigint, OUT all_frozen bigint)
+RETURNS record
+AS 'MODULE_PATHNAME', 'pg_visibility_map_summary'
+LANGUAGE C STRICT;
+
+-- Show tupleids of non-frozen tuples if any in all_frozen pages
+-- for a relation.
+CREATE FUNCTION pg_check_frozen(regclass, t_ctid OUT tid)
+RETURNS SETOF tid
+AS 'MODULE_PATHNAME', 'pg_check_frozen'
+LANGUAGE C STRICT;
+
+-- Show tupleids of dead tuples if any in all_visible pages for a relation.
+CREATE FUNCTION pg_check_visible(regclass, t_ctid OUT tid)
+RETURNS SETOF tid
+AS 'MODULE_PATHNAME', 'pg_check_visible'
+LANGUAGE C STRICT;
+
+-- Don't want these to be available to public.
+REVOKE ALL ON FUNCTION pg_visibility_map(regclass, bigint) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_visibility(regclass, bigint) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_visibility_map(regclass) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_visibility(regclass) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_visibility_map_summary(regclass) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_check_frozen(regclass) FROM PUBLIC;
+REVOKE ALL ON FUNCTION pg_check_visible(regclass) FROM PUBLIC;
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 5e5c7cc..e746e27 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -25,14 +25,24 @@ typedef struct vbits
 	uint8		bits[FLEXIBLE_ARRAY_MEMBER];
 } vbits;
 
+typedef struct tupleids
+{
+	BlockNumber next;
+	BlockNumber count;
+	ItemPointer tids;
+} tupleids;
+
 PG_FUNCTION_INFO_V1(pg_visibility_map);
 PG_FUNCTION_INFO_V1(pg_visibility_map_rel);
 PG_FUNCTION_INFO_V1(pg_visibility);
 PG_FUNCTION_INFO_V1(pg_visibility_rel);
 PG_FUNCTION_INFO_V1(pg_visibility_map_summary);
+PG_FUNCTION_INFO_V1(pg_check_frozen);
+PG_FUNCTION_INFO_V1(pg_check_visible);
 
 static TupleDesc pg_visibility_tupdesc(bool include_blkno, bool include_pd);
 static vbits *collect_visibility_data(Oid relid, bool include_pd);
+static tupleids *collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen);
 
 /*
  * Visibility map information for a single block of a relation.
@@ -259,6 +269,66 @@ pg_visibility_map_summary(PG_FUNCTION_ARGS)
 }
 
 /*
+ * Return the tids of non-frozen tuples present in frozen pages.  All such
+ * tids' indicates corrupt tuples.
+ */
+Datum
+pg_check_frozen(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	tupleids   *info;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		Oid			relid = PG_GETARG_OID(0);
+		MemoryContext oldcontext;
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+		funcctx->user_fctx = collect_corrupt_items(relid, false, true);
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	info = (tupleids *) funcctx->user_fctx;
+
+	if (info->next < info->count)
+		SRF_RETURN_NEXT(funcctx, PointerGetDatum(&info->tids[info->next++]));
+
+	SRF_RETURN_DONE(funcctx);
+}
+
+/*
+ * Return the tids of dead tuples present in all visible pages.  All such
+ * tids' indicates corrupt tuples.
+ */
+Datum
+pg_check_visible(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	tupleids   *info;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		Oid			relid = PG_GETARG_OID(0);
+		MemoryContext oldcontext;
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+		funcctx->user_fctx = collect_corrupt_items(relid, true, false);
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	info = (tupleids *) funcctx->user_fctx;
+
+	if (info->next < info->count)
+		SRF_RETURN_NEXT(funcctx, PointerGetDatum(&info->tids[info->next++]));
+
+	SRF_RETURN_DONE(funcctx);
+}
+
+/*
  * Helper function to construct whichever TupleDesc we need for a particular
  * call.
  */
@@ -348,3 +418,117 @@ collect_visibility_data(Oid relid, bool include_pd)
 
 	return info;
 }
+
+/*
+ * Collect dead items on all visible pages and or non frozen items in the all
+ * frozen pages for a relation.
+ */
+static tupleids *
+collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
+{
+	Relation	rel;
+	BlockNumber nblocks;
+	tupleids   *info;
+	BlockNumber blkno;
+	uint64		nallocated;
+	uint64		count_corrupt_items = 0;
+	Buffer		vmbuffer = InvalidBuffer;
+	BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);
+
+	rel = relation_open(relid, AccessShareLock);
+
+	nblocks = RelationGetNumberOfBlocks(rel);
+
+	/*
+	 * Guess an initial array size, we don't expect many corrupted tuples, so
+	 * start with smaller number.  Having initial size as MaxHeapTuplesPerPage
+	 * allows us to check whether tids array needs to be enlarged at page
+	 * level rather than at tuple level.
+	 */
+	nallocated = MaxHeapTuplesPerPage;
+	info = palloc0(sizeof(tupleids));
+	info->tids = palloc(nallocated * sizeof(ItemPointerData));
+	info->next = 0;
+
+	for (blkno = 0; blkno < nblocks; ++blkno)
+	{
+		bool		check_frozen = false;
+		bool		check_visible = false;
+
+		/* Make sure we are interruptible. */
+		CHECK_FOR_INTERRUPTS();
+
+		/* enlarge output array if needed. */
+		if (count_corrupt_items >= nallocated)
+		{
+			nallocated *= 2;
+			info->tids = repalloc(info->tids, nallocated * sizeof(ItemPointerData));
+		}
+
+		if (all_frozen && VM_ALL_FROZEN(rel, blkno, &vmbuffer))
+			check_frozen = true;
+		if (all_visible && VM_ALL_VISIBLE(rel, blkno, &vmbuffer))
+			check_visible = true;
+
+		/* collect the non-frozen tuples on a frozen page. */
+		if (check_visible || check_frozen)
+		{
+			Buffer		buffer;
+			Page		page;
+			OffsetNumber offnum,
+						maxoff;
+
+			buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
+										bstrategy);
+			LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+			page = BufferGetPage(buffer);
+			maxoff = PageGetMaxOffsetNumber(page);
+
+			for (offnum = FirstOffsetNumber;
+				 offnum <= maxoff;
+				 offnum = OffsetNumberNext(offnum))
+			{
+				HeapTupleHeader tuphdr;
+				ItemId		itemid;
+
+				itemid = PageGetItemId(page, offnum);
+
+				/* Unused or redirect line pointers are of no interest. */
+				if (!ItemIdIsUsed(itemid) || ItemIdIsRedirected(itemid))
+					continue;
+
+				/*
+				 * Count dead item as corrupt. We don't expect dead items on
+				 * all visible or all frozen pages.
+				 */
+				if (ItemIdIsDead(itemid))
+				{
+					ItemPointerData tid;
+
+					ItemPointerSet(&tid, blkno, offnum);
+					info->tids[count_corrupt_items++] = tid;
+					continue;
+				}
+
+				if (check_frozen)
+				{
+					tuphdr = (HeapTupleHeader) PageGetItem(page, itemid);
+					if (heap_tuple_needs_eventual_freeze(tuphdr))
+						info->tids[count_corrupt_items++] = tuphdr->t_ctid;
+				}
+			}
+
+			UnlockReleaseBuffer(buffer);
+		}
+	}
+
+	/* Clean up. */
+	if (vmbuffer != InvalidBuffer)
+		ReleaseBuffer(vmbuffer);
+	relation_close(rel, AccessShareLock);
+
+	info->count = count_corrupt_items;
+
+	return info;
+}
diff --git a/contrib/pg_visibility/pg_visibility.control b/contrib/pg_visibility/pg_visibility.control
index 1d71853..f93ed01 100644
--- a/contrib/pg_visibility/pg_visibility.control
+++ b/contrib/pg_visibility/pg_visibility.control
@@ -1,5 +1,5 @@
 # pg_visibility extension
 comment = 'examine the visibility map (VM) and page-level visibility info'
-default_version = '1.0'
+default_version = '1.1'
 module_pathname = '$libdir/pg_visibility'
 relocatable = true
diff --git a/doc/src/sgml/pgvisibility.sgml b/doc/src/sgml/pgvisibility.sgml
index 48b003d..feb9597 100644
--- a/doc/src/sgml/pgvisibility.sgml
+++ b/doc/src/sgml/pgvisibility.sgml
@@ -30,9 +30,10 @@
 
  <para>
   Functions which display information about <literal>PD_ALL_VISIBLE</>
-  are much more costly than those which only consult the visibility map,
-  because they must read the relation's data blocks rather than only the
-  (much smaller) visibility map.
+  and check all-visible or all-frozen pages are much more costly
+  than those which only consult the visibility map, because they must
+  read the relation's data blocks rather than only the (much smaller)
+  visibility map.
  </para>
 
  <sect2>
@@ -92,6 +93,28 @@
      </para>
     </listitem>
    </varlistentry>
+  
+   <varlistentry>
+    <term><function>pg_check_frozen(regclass, t_ctid OUT tid) returns setof tid</function></term>
+
+    <listitem>
+     <para>
+      Returns the tupleids of non-frozen tuples present in the all-frozen pages
+      for a relation.
+     </para>
+    </listitem>
+   </varlistentry>
+     
+    <varlistentry>
+    <term><function>pg_check_visible(regclass, t_ctid OUT tid) returns setof tid</function></term>
+
+    <listitem>
+     <para>
+      Returns the tupleids of dead tuples present in the all-visible pages
+      for a relation.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
 
   <para>
