diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 43aace7811f12095cb690e15801dae082cfbaf53..fc8b44a59ff114ad4e39787735e052cc255ee247 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,12 @@
+2014-05-05  Yao Qi  <yao@codesourcery.com>
+	    Pedro Alves  <palves@redhat.com>
+
+	* tracefile-tfile.c (tfile_xfer_partial): Record the lowest
+	address of blocks that intersects the requested range.  Trim
+	LEN up to LOW_ADDR_AVAILABLE if read from executable read-only
+	sections.
+	* ctf.c (ctf_xfer_partial): Likewise.
+
 2014-05-05  Yao Qi  <yao@codesourcery.com>
 
 	* printcmd.c (display_command): Remove the check to
diff --git a/gdb/ctf.c b/gdb/ctf.c
index bac7c28425cdd848828220dd8dfb1c862099d098..84d0a4817cee6b126e2542c9202ea2a2390250d6 100644
--- a/gdb/ctf.c
+++ b/gdb/ctf.c
@@ -1328,6 +1328,9 @@ ctf_xfer_partial (struct target_ops *ops, enum target_object object,
       struct bt_iter_pos *pos;
       int i = 0;
       enum target_xfer_status res;
+      /* Records the lowest available address of all blocks that
+	 intersects the requested range.  */
+      ULONGEST low_addr_available = 0;
 
       gdb_assert (ctf_iter != NULL);
       /* Save the current position.  */
@@ -1410,6 +1413,10 @@ ctf_xfer_partial (struct target_ops *ops, enum target_object object,
 		}
 	    }
 
+	  if (offset < maddr && maddr < (offset + len))
+	    if (low_addr_available == 0 || low_addr_available > maddr)
+	      low_addr_available = maddr;
+
 	  if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
 	    break;
 	}
@@ -1419,7 +1426,9 @@ ctf_xfer_partial (struct target_ops *ops, enum target_object object,
 
       /* Requested memory is unavailable in the context of traceframes,
 	 and this address falls within a read-only section, fallback
-	 to reading from executable.  */
+	 to reading from executable, up to LOW_ADDR_AVAILABLE  */
+      if (offset < low_addr_available)
+	len = min (len, low_addr_available - offset);
       res = exec_read_partial_read_only (readbuf, offset, len, xfered_len);
 
       if (res == TARGET_XFER_OK)
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index bd3829cc747f83c082f50e621625a0969589ca0f..311b52862daec29d5e5c28225a7be98239725909 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,15 @@
+2014-05-05  Yao Qi  <yao@codesourcery.com>
+
+	* gdb.trace/unavailable.exp (gdb_collect_args_test): Save
+	traceframes into tfile and ctf trace files.  Read data from
+	trace file and test collected data.
+	(gdb_collect_locals_test): Likewise.
+	(gdb_unavailable_registers_test): Likewise.
+	(gdb_unavailable_floats): Likewise.
+	(gdb_collect_globals_test): Likewise.
+	(top-level): Append "ctf" to trace_file_targets if GDB
+	supports.
+
 2014-05-05  Yao Qi  <yao@codesourcery.com>
 
 	* gdb.trace/unavailable.exp (gdb_collect_args_test): Move some
diff --git a/gdb/testsuite/gdb.trace/unavailable.exp b/gdb/testsuite/gdb.trace/unavailable.exp
index d76b54da9152c43f2b6cdead9626a70fc5ed9384..ec5ed265f9c6716ec03862bf74676d5100f5648c 100644
--- a/gdb/testsuite/gdb.trace/unavailable.exp
+++ b/gdb/testsuite/gdb.trace/unavailable.exp
@@ -188,6 +188,8 @@ proc gdb_collect_args_test_1 {} {
 proc gdb_collect_args_test {} {
     with_test_prefix "unavailable arguments" {
 	global gdb_prompt
+	global testfile srcdir subdir binfile
+	global trace_file_targets
 
 	prepare_for_trace_test
 
@@ -203,6 +205,30 @@ proc gdb_collect_args_test {} {
 	gdb_test "tfind none" \
 	    "#0  end .*" \
 	    "cease trace debugging"
+
+	set tracefile [standard_output_file ${testfile}]
+	gdb_test "tsave ${tracefile}.args.tfile" \
+	    "Trace data saved to file '${tracefile}.args.tfile'\.\\r" \
+	    "tsave ${testfile}.args.tfile"
+	gdb_test "tsave -ctf ${tracefile}.args.ctf" \
+	    "Trace data saved to directory '${tracefile}.args.ctf'\.\\r" \
+	    "save ctf trace file"
+
+	foreach target_name ${trace_file_targets} {
+	    # Restart GDB and read the trace data in ${TARGET_NAME} target.
+	    gdb_exit
+	    gdb_start
+	    gdb_reinitialize_dir $srcdir/$subdir
+	    gdb_file_cmd $binfile
+	    gdb_test "target ${target_name} ${tracefile}.args.${target_name}" ".*" \
+		"change to ${target_name} target"
+
+	    with_test_prefix "${target_name}" {
+		gdb_test "tfind start" "#0  args_test_func .*" \
+		    "tfind test frame"
+		gdb_collect_args_test_1
+	    }
+	}
     }
 }
 
@@ -244,6 +270,8 @@ proc gdb_collect_locals_test_1 { func } {
 proc gdb_collect_locals_test { func msg } {
     with_test_prefix "unavailable locals: $msg" {
 	global gdb_prompt
+	global testfile srcdir subdir binfile
+	global trace_file_targets
 
 	prepare_for_trace_test
 
@@ -261,6 +289,30 @@ proc gdb_collect_locals_test { func msg } {
 	gdb_test "tfind none" \
 	    "#0  end .*" \
 	    "cease trace debugging"
+
+	set tracefile [standard_output_file ${testfile}]
+	gdb_test "tsave ${tracefile}.locals.tfile" \
+	    "Trace data saved to file '${tracefile}.locals.tfile'\.\\r" \
+	    "tsave ${testfile}.locals.tfile"
+	gdb_test "tsave -ctf ${tracefile}.locals.ctf" \
+	    "Trace data saved to directory '${tracefile}.locals.ctf'\.\\r" \
+	    "save ctf trace file"
+
+	foreach target_name ${trace_file_targets} {
+	    # Restart GDB and read the trace data in ${TARGET_NAME} target.
+	    gdb_exit
+	    gdb_start
+	    gdb_reinitialize_dir $srcdir/$subdir
+	    gdb_file_cmd $binfile
+	    gdb_test "target ${target_name} ${tracefile}.locals.${target_name}" ".*" \
+		"change to ${target_name} target"
+
+	    with_test_prefix "${target_name}" {
+		gdb_test "tfind start" "#0  $func .*" \
+		    "tfind test frame"
+		gdb_collect_locals_test_1 $func
+	    }
+	}
     }
 }
 
@@ -302,6 +354,8 @@ proc gdb_unavailable_registers_test_1 { } {
 
 proc gdb_unavailable_registers_test { } {
     with_test_prefix "unavailable registers" {
+	global testfile srcdir subdir binfile
+	global trace_file_targets
 
 	prepare_for_trace_test
 
@@ -318,6 +372,30 @@ proc gdb_unavailable_registers_test { } {
 	gdb_unavailable_registers_test_1
 
 	gdb_test "tfind none" "#0  end .*" "cease trace debugging"
+
+	set tracefile [standard_output_file ${testfile}]
+	gdb_test "tsave ${tracefile}.registers.tfile" \
+	    "Trace data saved to file '${tracefile}.registers.tfile'\.\\r" \
+	    "tsave ${testfile}.registers.tfile"
+	gdb_test "tsave -ctf ${tracefile}.registers.ctf" \
+	    "Trace data saved to directory '${tracefile}.registers.ctf'\.\\r" \
+	    "save ctf trace file"
+
+	foreach target_name ${trace_file_targets} {
+	    # Restart GDB and read the trace data in ${TARGET_NAME} target.
+	    gdb_exit
+	    gdb_start
+	    gdb_reinitialize_dir $srcdir/$subdir
+	    gdb_file_cmd $binfile
+	    gdb_test "target ${target_name} ${tracefile}.registers.${target_name}" ".*" \
+		"change to ${target_name} target"
+
+	    with_test_prefix "${target_name}" {
+		gdb_test "tfind start" "#0  globals_test_func .*" \
+		    "tfind test frame"
+		gdb_unavailable_registers_test_1
+	    }
+	}
     }
 }
 
@@ -340,6 +418,9 @@ proc gdb_unavailable_floats_1 { } {
 
 proc gdb_unavailable_floats { } {
     with_test_prefix "unavailable floats" {
+	global testfile srcdir subdir binfile
+	global trace_file_targets
+
 	prepare_for_trace_test
 
 	# We'll simply re-use the globals_test_function for this test
@@ -355,6 +436,30 @@ proc gdb_unavailable_floats { } {
 	gdb_unavailable_floats_1
 
 	gdb_test "tfind none" "#0  end .*" "cease trace debugging"
+
+	set tracefile [standard_output_file ${testfile}]
+	gdb_test "tsave ${tracefile}.floats.tfile" \
+	    "Trace data saved to file '${tracefile}.floats.tfile'\.\\r" \
+	    "tsave ${testfile}.floats.tfile"
+	gdb_test "tsave -ctf ${tracefile}.floats.ctf" \
+	    "Trace data saved to directory '${tracefile}.floats.ctf'\.\\r" \
+	    "save ctf trace file"
+
+	foreach target_name ${trace_file_targets} {
+	    # Restart GDB and read the trace data in ${TARGET_NAME} target.
+	    gdb_exit
+	    gdb_start
+	    gdb_reinitialize_dir $srcdir/$subdir
+	    gdb_file_cmd $binfile
+	    gdb_test "target ${target_name} ${tracefile}.floats.${target_name}" ".*" \
+		"change to ${target_name} target"
+
+	    with_test_prefix "${target_name}" {
+		gdb_test "tfind start" "#0  globals_test_func .*" \
+		    "tfind test frame"
+		gdb_unavailable_floats_1
+	    }
+	}
     }
 }
 
@@ -551,6 +656,9 @@ proc gdb_collect_globals_test_1 { } {
 
 proc gdb_collect_globals_test { } {
     with_test_prefix "collect globals" {
+	global testfile binfile srcdir subdir
+	global trace_file_targets
+
 	prepare_for_trace_test
 
 	set testline [gdb_get_line_number "set globals_test_func tracepoint here"]
@@ -596,6 +704,31 @@ proc gdb_collect_globals_test { } {
 	gdb_test "tfind none" \
 	    "#0  end .*" \
 	    "cease trace debugging"
+
+	set tracefile [standard_output_file ${testfile}]
+	gdb_test "tsave ${tracefile}.globals.tfile" \
+	    "Trace data saved to file '${tracefile}.globals.tfile'\.\\r" \
+	    "tsave ${testfile}.globals.tfile"
+	gdb_test "tsave -ctf ${tracefile}.globals.ctf" \
+	    "Trace data saved to directory '${tracefile}.globals.ctf'\.\\r" \
+	    "save ctf trace file"
+
+	foreach target_name ${trace_file_targets} {
+	    # Restart GDB and read the trace data in ${TARGET_NAME} target.
+	    gdb_exit
+	    gdb_start
+	    gdb_reinitialize_dir $srcdir/$subdir
+	    gdb_file_cmd $binfile
+	    gdb_test "target ${target_name} ${tracefile}.globals.${target_name}" ".*" \
+		"change to ${target_name} target"
+
+	    with_test_prefix "${target_name}" {
+		gdb_test "tfind start" "#0  globals_test_func .*" \
+		    "tfind test frame"
+		gdb_collect_globals_test_1
+	    }
+	}
+
     }
 }
 
@@ -617,6 +750,15 @@ if { ![gdb_target_supports_trace] } then {
     return 1
 }
 
+set trace_file_targets [list "tfile"]
+gdb_test_multiple "target ctf" "" {
+    -re "Undefined target command: \"ctf\"\.  Try \"help target\"\.\r\n$gdb_prompt $" {
+    }
+    -re "No CTF directory specified.*\r\n$gdb_prompt $" {
+	lappend trace_file_targets "ctf"
+    }
+}
+
 # Body of test encased in a proc so we can return prematurely.
 gdb_trace_collection_test
 
diff --git a/gdb/tracefile-tfile.c b/gdb/tracefile-tfile.c
index efa69b260d1fe3f4cdd69d4477aa3356c4ad9593..37dc69191c2056253e8ff21dd832bc4c4929ca5a 100644
--- a/gdb/tracefile-tfile.c
+++ b/gdb/tracefile-tfile.c
@@ -853,6 +853,9 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
     {
       int pos = 0;
       enum target_xfer_status res;
+      /* Records the lowest available address of all blocks that
+	 intersects the requested range.  */
+      ULONGEST low_addr_available = 0;
 
       /* Iterate through the traceframe's blocks, looking for
 	 memory.  */
@@ -886,13 +889,19 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
 	      return TARGET_XFER_OK;
 	    }
 
+	  if (offset < maddr && maddr < (offset + len))
+	    if (low_addr_available == 0 || low_addr_available > maddr)
+	      low_addr_available = maddr;
+
 	  /* Skip over this block.  */
 	  pos += (8 + 2 + mlen);
 	}
 
       /* Requested memory is unavailable in the context of traceframes,
 	 and this address falls within a read-only section, fallback
-	 to reading from executable.  */
+	 to reading from executable, up to LOW_ADDR_AVAILABLE.  */
+      if (offset < low_addr_available)
+	len = min (len, low_addr_available - offset);
       res = exec_read_partial_read_only (readbuf, offset, len, xfered_len);
 
       if (res == TARGET_XFER_OK)